From 57d7ef74c2715f5cfd41ea9d5936fa355227bb0b Mon Sep 17 00:00:00 2001 From: Brendan Westley Date: Sun, 8 Sep 2024 17:07:02 -0500 Subject: [PATCH 01/15] Remove LeafletForBlazor and display and add waypoints in leaflet --- .../DesignTimeBuild/.dtbcache.v2 | Bin 248823 -> 250969 bytes .vs/Basestation_Software/v17/.futdcache.v2 | Bin 529 -> 1081 bytes .vs/Basestation_Software/v17/fileList.bin | Bin 327366 -> 347965 bytes .../basestation_software.metadata.v7.bin | Bin 0 -> 385509 bytes .../basestation_software.metadata.v9.bin | Bin 2257 -> 0 bytes .../basestation_software.projects.v7.bin | Bin 0 -> 2180809 bytes .../basestation_software.projects.v9.bin | Bin 306045 -> 0 bytes Basestation_Software.Web/Core/App.razor | 4 + .../Core/Components/RoverMap.razor | 219 ++++-- Basestation_Software.Web/Core/Pages/RED.razor | 2 +- Basestation_Software.Web/wwwroot/leaflet.css | 656 ++++++++++++++++++ Basestation_Software.Web/wwwroot/leaflet.js | 6 + .../wwwroot/leafletcontextmenu.css | 54 ++ .../wwwroot/leafletcontextmenu.js | 586 ++++++++++++++++ 14 files changed, 1457 insertions(+), 70 deletions(-) create mode 100644 .vs/ProjectEvaluation/basestation_software.metadata.v7.bin delete mode 100644 .vs/ProjectEvaluation/basestation_software.metadata.v9.bin create mode 100644 .vs/ProjectEvaluation/basestation_software.projects.v7.bin delete mode 100644 .vs/ProjectEvaluation/basestation_software.projects.v9.bin create mode 100644 Basestation_Software.Web/wwwroot/leaflet.css create mode 100644 Basestation_Software.Web/wwwroot/leaflet.js create mode 100644 Basestation_Software.Web/wwwroot/leafletcontextmenu.css create mode 100644 Basestation_Software.Web/wwwroot/leafletcontextmenu.js diff --git a/.vs/Basestation_Software/DesignTimeBuild/.dtbcache.v2 b/.vs/Basestation_Software/DesignTimeBuild/.dtbcache.v2 index 42cc9cb18bf9512a4be13d7857f3b3d5456a84a3..c770ed1ddc02d0fd1685a7bd47f4fd211211083b 100644 GIT binary patch literal 250969 zcmdRX2VmUBl{RF>v17;Gj!Rr*y2V8hdy(zL6e(G>)l5>7?W`kXcR_Nc-NhQ%B}F+- z@9h$&w@Y%l-h1!8_uhLi(BVq%F1h=^H)w+eu!{v}ApcIDX1EyOy*K5}n>TOXJbj^} zD3{Lb<<9DBXPcWl=8hbFt#Z?zPOV;@(<*EFoY8D+wWf7MGut(-KCiVk<@|kG-O`mS zkC?_uz0%%SYp&{M%d9oq%0t!BnNfXsYGOrOnbKxP^wDW;rdl4?$EIe6r$(l08__1p`dFnpGrm$DozS)6;c?{}hIVPQSy?lUX6=k_&KXr***2!!wy0Z1 zeO>p^F6t|~sW&USHLqK>)#h@op^qx>-8Q$obkx#KtF#ACt6KB8Znf+BX2~)-W<@WV zddt8w&BD)GyJj>`EEy~94b9X~?5S&K3^VbWdJsg^tx~zxEV0TeHTCwiI^3(UTCLZW z#UxDL*Y4YK40M8o2S?Ol`rmy!<~sFu$J8He>YcW!)$iMJq*JcfDv#=$%f>0a`Czky zZ@#E)c64P{ReNFY^-Qf;Ewr6-vsa^YMkA>dL1pi)LC6cb?MdPhIy)PXjMy5 zy+_^`s`x~!R%aEO_^A)bZ5N|_GTJVtzS6S>sBJ!7FikzWg4ZIb)z&6eWnFLU%9Ckb zS#5S!^>(SHRZeMWm{OT_T)nEc4b5to#??`Eq*Sk!gHGx{U#91*Up}4n+jU)A0W~Xz z>0&93qNh%()Bg9TFbEE~W%e1SJA@d97BFC_u9{k7$G%z}WLAwfMhzp|DjlpbQ&7UR?(OV>1NSL56`0e zF@E2aD!RI5Ln9bxn$^N+s?a#o@EyafongJ0jkZyLJkz9>Jlgno7!k$rAJC~DF)$Un zL;X4FRZmjbXkx5eD%E;O1-1NZfj&{m$W;54;L{nao};Che#ar)+pM+>sCMoMf5+DP zUC6l#!QHS1=O1%S!zE?C$S$y?o9nvCdi`63Rl$Z{9=rmM>t#m9SBNA-vn;()u5YSP zoWUofYO=3H;CczDn)ScI)m0J0wAqA)$|m60Z2U|CU4&*oPB7I9Kxw)I;Ea4!=4Pu+ z8eD5_Q(b7bb+f6}S?`mt=gjfAvHqFY&Dy%Fb&(7#K-Z(SF0{$E+r8qto_w97ivr^N zbQh_Api!X07T~Y%yF~oNC^X-f0)AWE*U| zCJOmI=YUbs>Yfp&peZ$ia?$9p`IRIBnf~%yL>hWqt3ut*xEwy{6)#BI9$c@)rx^Jn zkfCkW_MUFlVY73M5AypkVP&>>YSqHfxsp%5{g}9;1(VpV3bq1<3|A59z|f z5*x7c_c%g?w84SB4%$m%xaDi39_~jWt4zHfs@;XPt&f=`1l!t`D;vA`T{g8Qgr(V5 z4`~g(dZgC!lp5yUaE?fMtW$Fp^|#8OD^lxY$*7#tJ#&zJv9iR?kOg!-_-bnPMfCQ- zU@Bk9S_+VQ4nqahs)v%smVB=)pFxb8wox(a+`3P`rEQ6p(iP_9$0VoxX#)NR{1}nn zfq?L6yHvTo8w{MA+rPFftKCicM>;~s@HW!+7I*3+N zF<~odY1M}e8fo%79`8UDV_cFh#xu^jPZ7iKk}qg^jw23CG4_kC{Rk~o7m~KKxZ01{ ztL5u7B+#JouVYsorukTPOLBwZ`Hc_&#R7-xd{dmHMNcaBy^XS7t>P3EHFeR*kF0H4 zI2Gh>c*&nuso`OiYwL}N>qZ%;5~cdoaaHbnn#KB>qy=>XYHD6dzD_(gas?t(^0ni@ zZ6w%tm$XN~u@~T6YQwp})r941MUg$)SQnDCis!hF;HSowzv}b-tYX|sGHpk=bzQuo zx<9ckzkm5`12+zB7q!mxv9crDZ1l<(OFmAjS4o}@32nM}9Sd2YW=zUgr+p?F+9h_9 zzF^GQEU4%lW=S?iuBs20Y5j7OH>EQtq!~JqEFF%f3pAO<$erq2wnGeRgHy6rZsKKJVw^>103D2p9PI?SjwI?kc z%+yVz)vHDySz;%*3|E_x%~#+obOXjOuV{8-KEXmvzVz%E^&E|n!Cq8=!N znd7zQ*r?jo^K=Q!xQG_@)f#r1UH9ap!q7CVieVz!1e*{Z7xr14RY!ytL~t?P72OGZ zIn($sV1TNuTeOujj#wBY6k26;cyxStawIs@I*C*6hf!{BJ=}76A}az@tDe!DsuvGq zTpb%z$N0mti|%tS7RGCY)r!05qd*y$B-)Uztkz#G`ulr2D~PV+E(iO({XAkMKx+Q8r&pS*M^w z)maPiv{%+?iFJs^_&ek1Di* z2p@zIG{Jrow>Zx{9c79AmB3rX`jL5Um{rE`?2)1U7&IX$D+|pPgNYb%OMwK|p#31E zp(M?V=L}9;l~c^B!kEguXOX8Cm!YU0Cm(H-Av}O(2q`ZNvD87Z4C{?smzld{F8;cV zQ&M{fRiVG(vm|=9BMC?YD$`2KVmnZ3)zmhKAe^?q5qK=QrqoeCcpsLAy4GrqVZG&d zcCR$&|0#Szo%e7MH!Gs6$p3hks#6h;XS1mxdYj6houROeLf1#`aXsuETg?$R9EEEM zK89t@9q+;iBJF^N=b-1bI{Z&GZ|y2v8;l~O{*`%$A**1CqUh3lgs)#04wDKOoRZoD z|KYmp9434T)P;&FPxx}A3rDz5&N{bn4Ydp1leHFMWVnur9;(3QOJ|`X0N>@RX|_V@ z2h&r%jSt~tD2kAH5-t;>DCQA5Ob^sI;R7~`tZQ13F<`moIS>Fyr+CJ6!TumVE-s{khQEGnxR494tUmtq4R{xQHBM9`85P-clW4;PuUE`V;w}; z4$RsVm0alj!<_;-F8bTFFG&-O@NpMxc+%H}w`}nBz+ql^7xhs@*5XpC>7Z6~d#CXE ziFlx+qd0nQ)DerwJGd)c%VT)-_6BZ0AT5GItN6I2)YuV!c`tlC6DM5?iWCllQ?$Yt zti*vA-@%=}rC`8ejSlQp?pC>!u}5crYZZhJh6@K1QNkDAR8#yRNhwkaUMzF$GGv*o_O z7cq<2Koq{e)Qx1}0P}SSXI%?!=2w&^?AMfhw;sw0d zUi;yC>6_t%t93pzo@a8>gNo6(PfAly0>*b3t2e&NzNz%^IbYTiXy!dopw!r#F&TVKT` zXzeu*t8ncn8S9*PvbiX9mU~P_xM)e{jt04KFW_WU?5vS*SIm6UHtSrMjFV1SELpDc zx1e6yC0jBdvfUb^k53HG%)tk_wn8_9$~Z#i?O!F=voG3&n{oqm)KP^iD8bj!Xw6Ek zc$3RPPk?ra^_;Awh}%QbtCB#3(~7F;w&P3GUB*nO~)Ea zVt;q>1pyjlb_v{F;96mYZ#u%8ABIrw3VHEzj>o_v;IM`fzV&oWt9SI}PRsLu74FO9 zPin&3nLbRx3)}#|?$)nx<1nvr3a=Vz3jq2s^8q7qC|qFB*WC>jZU;j6c_6|PHFyy- z!gWeQ^Z2|`frZlZ=n&pf8Hz?df<#Jn0g+#0p4=0z6)|3pXzev7jD&A`hM$kNi8!ZK zZ$Q)eYR1BKo_op>9IJ!BEOsb$gf+l|Nx9UG1cB4@%Z6sPmGj5dQFTPQ{D2O9MsLID z>lRQh7)7XxNp)HoJy@%lhGnd@aWJO^uMy^|N?T|~6TOQm_+9s{E9Xzs%gRf+uomOW zWnJ+vlsovxs_9uuJ7r`#>L~D!?>s#@u3S0}I)-;H9hyC|wEyt3a!umlE#d@cy_8EL z5igV%yCE@@+nHb9Iu>%QMuNz&2%$c&TL_Q8jA>Q5I$Ars+{$?%s|#?sP8V>YZrfD1 zIDAG`dtGKIRD|F$??v{z()K)Io#XdHzeD=IuRec3V zB6cq?&d%+lli@*AURZ;YNuePcBFQ)6kL8jE~Nxx%eL94@Tm-c|DFQeL(T9{v#X z5I>z-U2CrH)|)$zE-9DoT7%~h1T{i)ZLVwgDwpqSqEX0&9wjJIKDBKZdzEI0-D#c4 zbppXrT13??(L{rQNQC&r5@rUpoPNTUor%v>W)!qaM)_ok#R4cnl0Xo#letMB^Il++ znVx|$109a5cVPEy?l!ofWOnG z9!UEUs|!cWmRc#)b~82+M;I;d3vM3{``%`?MTa`r=Hig@K?zl&Vq$4n zgI5eznx6N@n|Z3_Pexqz&|aczQy5adjHg0ScTur88oXwF+u}D#q9}2>=4&jeIs{j3 zAh2t}&%{vSqaXWZo~=5rQM`^D{6+LBnzDIOxLz=R_ee^Y)GL5--Ybhawcy`!4*sM@S9wB_04mk^3nz&l=`-J+9uvX3s$JQ=wVgzZeGC1?<-v-ME4sDS04EA zWAWB0c74Xv!dj+aQOe`*zNlzOd8SZN#`u4Ip(yDd87#aJ5zh0C8Y)_4OS{QtNZBmB zCW1OB*ht{lfv1$k$7I2J;{G-^(Ov*l;Od_^74IxobhB{!h{F^pT+jv)UeBHA-*k>? zVG^TQeT&rx!~4c_zOwui-z9T zs#v~cv|L2aaTh!n99*v?OpXQ1WJW}*1|Q+NfqevbQY>~%5D9ZYehzVaqNw2g3Sykr zm0i~~3@M#LWfGCW#L+ompA4y58;ci9^_}5|l(!VCmJt6O9pv!Ds^c7pgNTG=L_Aw~ zS6C!BAh!kzHuF$JFzmsBmBP2~h=gTrb-!M3dAIfqDd(Uc9INRYQ7yGtHTiI--RiXG zw8~mAMHj29bRUWsQZ@=52;3kjw&q>L{_%1q0t()N^J2oy>lJu5nQq)oR~)>rShL(20nkecGcOcuR=vc%8z zMR#5tV$kY~7(JnBzF3WODWJf&L=-!l`1sklE!cowD8(jnPn8p7=1FpRelC}N$SN*2j(?jftE$o7sTb&tb|y?&hg@`uwx zlik8qj9EFAif}?L!aXXL4EPov(M@V2chz=I2Q(dHL}Vuu_tU%7oYQ_1*!_!G3UCb+ zoaWtJXr4^Pv&dFQ_PyjXy4%GXJ7L9f?cKU%E#=j$Tk=oqhlD=i1RZ3S!mzWZvt#};4&;i|)@gY}96 zydrtj8~LQRtzJK5Aj}DfE|XbkO(oXMG78{ zzJq4GB8u2$iPXgED#@hiit1c+Qol+-39hI+Ck6nY1Qr9r6&1xwN1p&z|3!GEB=m&6 z>##R~5SRagS0#Q0Fd~-NMKb4<4s0Yj(z({oPht<5&`oY)@weVbDAwl1qB^6(Sy6;T^DS z2FESE2(sz1SeMBnkC+LOT>bWzp2)Y2>L z{w?3omg_V@c9y-kn(ieUu~RL%>?%2tC+dH4rpI7BMYv}TT_neBO+?WMtFzW-)9Y!u zOz%0XT!^lj6E|I53L%YyrBD#vLDrNz$oL&**49#|g^1Ndj+zj3W8eKv>Lftp6AQ=C z*-z?R$FR{qOM07I9kF(kTAK_pLnC#P<97y0SAki=-%^*OD|?-V$IDs8a;-A6j{FGD zU`LiW!sMD>`51+1;Yky?j+(=AnSb&yPWz0L)zEP-k4aQfF*N!FtH z#)fNXCZET4X|Z{9CMj4$<@Piao%mm%bKUxN*(*;grL>EkiP1i zR!5L_%{NC#)u^MGS#Qm?xt3MRMe%hjg;KS(g~O!z21jf6;Lt_gb5WJLLhZsElY7FA zSzQQ8Y4M~W!!1?i@523Ltra@GR%FTtAZFZ2)dhistIgPT*+Yudu^PqBxRolEqWIps7VCx=Z>A<$EOF;=We=L8 zO`Oz0*QvU#ii?|dt>N|~srDBChP~*~*#Q-L^o3{Cs;5|z245Oc$J8l)3(A|u7`l0{ zv z)Jj#98B)EyVVI{#siJUf0b=PDdn7RNNR=qWW4H1qowqQygj9P3@#ckW@)Q{9=x}DQ zgXlk=XN>d#Pf?AKDRN?2V=UAo{_!n$_^cfMxf?$62^*I%W&nQ^EYpSc#kS@kFm+1^IYld4j;ZM!ip z9AGX{oU$R+VAhTP0In5nZ|L-|EBd6$A|89_M9!L7>V_JJmk)G3q!h(%x6;LLvk6~a zaomzWP`6EAs$?XUy<@_p16@oD!lg)NH$>^?4lRQ};NMJ281RydMth zfel!yLhQ5Y9UBRH6_d{S0+I~cqq|W*qOEI76|>gz=B9jSakol^S1~c8Kp~wDRf7Ah zaJt1)Yo)5}eit}kgrZxn`1E@DAzz*TdRb}Cx&O{a3rL*g7`jde}6xhnPUKtg5(AXgss$lhX6P0_);~s z02NxgS*f-B07_D4@&u$ftXnlUJTP&~``0wwH0z(#kXy9@Qr2yydgv_&xaj)MI&;%=drvNv za)9|X#2mjAO72JvSYFnwQx?0bZ>4T*xNJ*ZyM#Qus9F0;omsmvb_=FLYdY#|CUtV< z_-4)RHf}*f$weLpa4M=#)v#miGAeb)A7{8=nyO+&wWes$8^- z!uvI9XY`%Q=q_CHx7#S6EV1|orKaAd5LEP81l6LM?ZlF?(%#VE`|n9-yf{aL8$VQ?{o!I!5L8?o?w{|V){f?U(q`Cwz|p=()#?K2`Jf8{iB0%bX52z;@4ANgL@{i*AT44 ze%)<_dA{5E-{UKQOhybd8bkH2wSo(17ctn+_g^lIm^c-m=cvefHVKE^z0s&yEl43c zm!wuGqJI-JqNVz5Szd6=@b9Y8M)^`&cOiEg?;DZIQoeFaRKlL8WB;Dw!$yOp#s~vj z_08tEIvypFTY&oUQzK%z^wnt^d@Z~bO_&DlJ^z-?^wIsI@?vTc7;;V^U|QR z+bp|+*uSBOV=B=5i>8J16hYuqG^E+?A}Q1G6Ine6YGqS1Hxr3L$QHkji)pK*9y6q5 zk^=eb#5)`q8a|V$klZpn6$vKMu5Nhz zVrd4IO{>#ddpDhxFCnn9yE0V95}${fX=x6pVoi;XPSAMu(dD_TXCd0dxm89d^W{b`|`Y+s_h~j{@34=iC zkhXqMZ+5(I-`k}bh2cYBX=i?au_l-fI=rPM;EQil(D-?}xCu0=44$=Pku z+Y*bMaP>~V(0z%8yeh+~?xvgdK6x|imY(l}Rhl!5Q#IZDQ>|FaZPDisA6fF7h%0nU zv=Dm8GI~5$;MQn?pkHuBE`sC5s)>jhKzjcGwh?A@IErf$RxNdvUVc^VWisXv0T_Ju zc@ZQZT!%>u@ysl}QLb;!w&B-OW)+PO%)~yBYWng&z(Vuj)mwPm)xEn99UYk*p15!6 ziKPPzdxnQcMwDxrT=xW*KCfFaz|ja%Ztz4&pc!({npiM0qHGj|zuPjRe0&=))2*;pX%Fm$Rjqj(X4bmCSyG!F*hEXTD1%~W zm5Miqd?E*JdQnD@=h9j%|0*`r{QDX{l($gbB#ugv`1=_<(#MTRD$?y6%IP9%A)@a2 zMzGWoo1b&ue!$$f5)r40Dlv%r87`D(vsO8`d?mdTXGPuJj?kcUM@N#mLHTfD z^hB2L{Y(t$a$9ugiIC+s7>(mM7FI8*mV_R|S#%}zw;srDkbVXOT4>cN4#&~fvVkiU z^tm;?g6LG(_2yOrm$G$KXhd*VXMKzYFJ~`tg?J-@yL96ob{hrU1tM3V%LuTe`|H8t z&R>wmQ1~lIzrc;`6KN~d>-neACI8FVqxcg4YVP-NNgw$&Si(ns4VLVYUxOw373{l_ zCHYnCr>-UV!`WAm6{}?$Csnque;fzqmQK}L!Hlb1f~9@ER;jhwbH%Oel#0UI*HW&f zf^Dd!C()G-GpC7%O{3FNK0@O?ahc)HJc!EG)LAToB#vs|T)b3PkMrm|-bQBg$+<4bSq`s2pEmfLxulXzOftb=7)yZ+r&5KGS z69^}JgRRl8>jT3$+L^v2H8m|uIO`$m5j5~ zUV{@69g-!30)Okg2pY^RNcZU26*XBqh``y*g3~qdTO#nC%7)L>MIc+rfJ_9QdImh| z;aT(^5!m!pY>1w4hILI5XwsPq(5OcYhy@|zua(GDxPnlr_5#Er_$&9pC;~@v9dcal zZC2f_OA*-C`f#K|6gPy7;#xmUv`43#BA7}Z!(0_&F(Cp^ryoneisw2i8ZguWI zp~dE%w0@7HvEF$w(X&i7g;CnW6*rzab%~p_vF;Vp{~F0W@WEu*W(?dYMD&g1iFuAk z2i<5M5u?_HiQi-mgzm}2LcQ(9UKQOpk_TH9sfY@FCb9jZc+}7aC<0~jK;!RC6kVCg zdMk&q7`70>y53%6mO&w7?|4$%iC`sJui0-{ZRq82zJs@h2sFuaGNGZU1fn~^iKN<6 zJ!~S_NtP2nJMKg!oU7#de}5Z?4@FmC8me|nI4UE#t#HB@tL|_@c9;Jm+AdjPqa(#U zLJ^pfr(?n-;_IvF6(?4Rr-%ruuzk8lHVYvlE?Grl?E*Usj3Yv@B+n0o#q(zu(KP8b zM&U`!T2)MqrSoEm(}Cs9GRd@stU{9;o`pz|zOv@AsI*-qXueCXH9yR`h(4Aq z#+bWa*@6MlUC4;Qlq_f%rdADsjOuXM6te0}sWhDDpshO*tn|bnM7|`O+NiQf(VlHK z4fkNS5M-}T9GgHUqOQ_8A&Z9AX5n$k_eun^lWCkV^NEKHhxA3@N*_7AG!TJFOXZ5W zcZfidym+Mk#!#>wce!|$wblK4y#=>e5qQ!Y=U7eO5UFn@PtxvA{G3)<6EqVf&u)CM zFcH=ERANsP*2|LX%Jot5;#Z5{B6*eQ-5)luSHgR#MPO?u*O_kN#JQz=T|oGHCwVr7 zhRb6W!Ctcb4iJg0%j6aJqP~I9(n4nLl&;}wY{DBUg&lGFVY1lv3+S7ya?A=)>0(0{4Uc!~vg8FDmx4{uIt-grPNvcp@%rYTRwJhr@}lG)DYAXwQh1P+4l z5f=oyR0c$~fvT0>P?N*y=e6R^2Ac;~3J*v2)tC(7et@x?ms6f0a8N~y^RPB`r0 zS~(FMa+{Q?9FqLR4P1y}DMYp87B^`qMR0Z|97Snp5MzG5W*SWjBY8~2RY(W`C`1k9 zmO81pN$`aaMR3gRB84Bu>_^e?{NB3ItcgIy*^g3DiJCpQP3=@%@Ju+On^dJDxXUsq z$ekTmKbX3heUdC~C&(Vh>498*&g-}&2D=QE&4nf%k&xm0$t@*%V;0ef$=!zYOb{-) zIW>o8ucoawY2&>jM6^7YDAstkcwY$NJoc|u9=(Wm=j$+)`l4m4pME8K< z!w83hGbf_GYq+}f-BCn$6>d2g$0nkwIKK%Vfgrz$WOdKq&=zJn;_SQ}69Jkf#03_k z+a)Up{8pB5VZkjsIo^1bBDz8H!ov?cD7sg3R?G-btE=Rp7ExM7FqhmteCB}8GQ{YP z$=%&Y#NvjF;3ruq2=uApHU<;gXYV?eOu#0Bm1M2ZuQ7(njW!}587#gPT^usbB*oR? z;WFvhbkB=OtCjqM=Q5aI&K?Rq0x7rPclg}N__?AL^3yT?~?thd(fj5IQq_4s4V z1iC801lmm~J8%3%A|qk<8DR%FvmB*~@lck`1KD!9X2!)CW25SphCqx38O_j5Cu;`I zUCVur08+ReBiC8l7#F&PhKN$hczCp7RSdIU7hp zquHm5(Rc_O1GxVL5&BGS={Tygqt^Y8i{Wr}d1P#5rBWTOR;v>u%HzVwe!eHLDipmcKCbQi-Cji zHJGs5`f2XGdgjyKu^##wVbvHTk5%Sl-p_kFD>#Rr$G8=1`B#f3*`tlo`?NGjh_@veln?yo1<}VM6hr|CJQvVoQTsM28b5HK}{wHkxkdI~0foHdTsd9F5mI@FsL`S{Xmppp18? zjeBSI)_lTaETx7Z&d*XBxs0o|WUf-zTbZ?mv61r1*z^SExUtD`eQY8<*s&nax=3c+ zJ)X#&2(cheaZV($SKBZX28Af9&RUzzJic79UY8n(T2R|`k<7TOBr`5{anKMiAtJ*m zq?YltlFZZcW=n@^fWu>0JB#RjEy-*}2xgoe4>;qt|3g+Xi-lO%)5NpFQOIcbHA^%> zPP1%`YT=rtM;oK}P*B&r17P+l?p~*fTL5u{F-m={kz_B7>^c1+W3?+>Yvkx-bo-hX z`_w zS*^d?8w`Ajy!$aRKE#=W(x_xS`14hShrq!k_s&IZAufEr>P0Z)>@^8|Z()}y#*ya+ zpz?%}cfc{2F5v4#_mqS~F~14G5IsBgpqJI#QwjBkB^H7r3K^f`jJ4@>GCl$cAM|ER zg@<_M6x)*%(KO;Ed?Klgn??e+bOoe)EjwfB08G(zQ?dxwws_+M=iHs0ZpLSlOkKv# zrjWiTRi-j384teQgCA*I+X#-$4#MpoDYPTs5(vLj;f>m_W)vZmB0S+zMQ89x{rSdU;p@AQ%#h7so~W` zxX2Dk&p)NEh;BTe^pH%}-=6Tf*lcPI>^tW_XdN3f72zyIX=L;t^XPZOMZjg%YL4Wn z|5$H)S~T%IfsAs0Rq8)j*7D4XF~WtfN;&!%-Tur$zNKU&XJ>A4=&4S(mF3zIi}f7g zGe-)oj33`-6e*LV3&pq)ZZnEd$Y?Kny+8V7Yc_&zAzBwb64Hoe9QiwC3x~;4(}Mdd zEmYJ!IG$P0bBHHq@({lEPNbCa;?I@Q5_!5-x9vl?T|Y`9qu#fCcRdQ7%R@%;>nOr4 z->K9xp8WMzSE(m!En+KVDEqwK-7>=0Tgjv{ZVq^(f8lT*%Dcz;G1223l#`z1+aT~~ z*mqR-vTbHn%jFUpleWTb5IyK+eEIW6q@0L`5I%22C}gzzb9nU0Jfa-yqHIFK=kOF- z89)9!7%da3x+0oI_&k_KEaQlq2f0ymTx(a>#8^*c%!6@C887}y2@Ee_YYVrTRq133 zZm|{7F2YwzaWWYfzO5FI-Lv+%*Se)wiDw~(pEU{cH%2Jq#9uRk6AJHA#}W5D9K@C$ z-0aH2#)ud4GG^BVQduo62D2-xr93qho1{{2>B{TD;5DJeDCVT#(I$cyV1QNK9V@wa5@C!|-zLm{nD-17(TJiWaE_hI;vPG^GCZYM z#>cDrq*j?88&#h4x+42Hf}wh=A|YL}Dq8Gx6UnN_SC-hrBO0!rs_S`90>ws@aAhe% zA*0=2#<3@lb#N^eh0GAq9fdFBq9iivk9d72cYK(bn151qk8w$3)E@Mx#Yvm6g0n9# zyVo?wMUzS=fM`+UPY(2V&RFZQt}J|V@QGs-`y(*;F=wQbSa%RU0wWYM+I{)wKiR@A zVvKy@@~@l7SbsUF)$)kG80Av>3lYaC_LX*Ghi=Y8Oi!bZ(B-*e%EkJ+aHTy?CgZ|a zN!eq)yrBLn@$SCJ$ zCyHlUtKhaZT$-9=bjD=7l-Y?Vl5yZ~MN*^W4;&e*df{7<9DR&#UZ=rn^0|5q0UAV9 zyA+)!ppen-n_!72JaJEyhezLXa1pD2;U?G=S{XmQ0}=(y}l!0rt>@$;=m z9HTgp{r-=~5alG_RhY4EBVP7*5y?34WxpGsg`E}@A$x?&evUpyw{Pskedu*Ba^rl> zYS9KsUfC0C0pZ5ZG-4S?{(<BctCx zT+sb&;Q%|)ocBh77&F2T7xW^SaprHlcNaWRTU~2!==86M4kmo#y$8LFFMqz~%bkW- zIXMamS?ma(uM;U{y!dX*T!}5*-xlk1!rhh==uB%9vCDm#Z4y$-c<~*dI9}%?kRw4v|a6P4)$gWliIdF3a*-obrRwT4a3biS%;vSWG zfwziFB;%mw?bmp2wTdbDY2D?&EQEYW*8eQ0LS@^z0oJ>~hf!5r?4Yv(l-H$+Gg8YcYYt2y_ z8U4QX%4g4ux9AUh5yL^a^(uu{#!tO#oOkh~KBBE_OBJ)$5~4D%%NptFWHTx$wwPCnz>-^t|K->hM^z}uHH@1Mo&lhPyx+R`JFap7C`d@c?cqVzZ6mc1y6 zjQT+T^km7Qn1wHM3&a*V;`PriA{ht4s?&Q2QO9D0br}fq8!E-GaFQrxymV_d5ni~< zkVS}bXjR99;}IBNVx3lM|5NiB^RC(<2EFG45d<=g(r&(Y@A<_D2gYm#dz{HibT#u zh;ZQ=MIxn)7e7E}atRjZUy#3=@F0-ANoL%wdh4d-q7+_x$;#u>8Jn4&5xrFENipNi z*9DVHhSRf%4q1>q42fnO(lzI8y`d&^`1r)|%p5{q)>d#SS3B21s8}Hv$AdqLVZ$wN|cs*kTPX+z1dSlX2lM zYZ4yg8ilH{Aw&-ozN|?nmvI%F2w8a@ht>qm8S*AVo>In(Z|J~+lN>?a=-Y5}$}vJ> zv8ja{I=YEu9QcMN?_n&gUj7j-+|U#ykx}m(rYY_mZaT*GLgb1*hg*h@;5ej^;UU~G zokA<)$9Hj!lnFaieq~pw*{ zNSsW@#geBGvBx=WVKL&L60P$D)G?YP;~G~Qb?TZa_#~{HaUG{M+LP9BGTW6=Yg)6Z z=L#YjYYg!{DMn_rC;5+a41<_5CB7&5)G?aktXqocXu{V4-9$1D z;&Re0mh%qRn3$I+CqqIRCkbO7J~u)f24#+UqLI<>AFV>1V0hD2=nOWukjcIt>pmJI z{Ad+VAfr6cmpRH+c7=D2aS)SI;4F6J4?5DPIxE{(8cKVM<@MFp)`)RYxR6*e`8fFd~|lv$msVSC}Re={BDg)R79zs?1?XaQ`Zi|zo8sDp5+gsc!Vd%b#4(EfIiG%X-Rob#?%dr(G7kJG zR`d|YHL(s(!f!T+QOS7lEoSue@p+>HPqSQy!ea`HaEn=lLPonU3)z!KT_p9wWnmYM zvHpYSeiss>Yf0%p5h@uEzIBZ8cSLKig@Px)wN>ncwe-+yEg&!kJpp)@& z$eXC>-pH`pGt2wfj)}NYOBp< zDDeupdmpuM43ZRlvb~#T8 z`{g~`($K5T&Z^!nQG`s|!71UE3e&JqL8@l8ntB^I#p`MXvEq#;IJeXdy{uW5-YC~M zRmQzpqH#M}YSR@xCAUV|6=>|7INAi;rn<>~*>6~Ge78HY75n51hicRHL97^bucX`l z3e#x(p~n2&?<-aF4Gh}>(52VSG?GYy;`NmYcGUWS=wxrjXH&I*IhlRN2_#~efK%* zRZxK1bgeR!2HNjtl;0ksJgJmK-OVVUEHSO41T6+C3Mq4Iq=4wBb>+q+b&&cKd;0d& zn%ufdS*|ssWTjHls+N1uNl&kI>sYU<%ZBEuR#zX;v3IDqF}B^O?kpcph_hA;0j?-^ zwmREsufZSRyJL!^z-@`;=&B%3{i8y6_bWtl#f_PMSHGfE<@C+T3ELYrMZT z@jXAt2mADnut>O#ryg!GLq98VZCK)wCGU_Ydq8I^6P5OKt$Cx?92-?F<`at*161bH zIK?F|)oW$yiCBZsMPAiW{1l?a`Uh{hjw9@bTDST*(o;%YM()vb>!B`m#iCX{qvJ}a zx^~8J>xg3GWa7f5TCI9l?m_Es7)?h0XcGCJpNoI>9eN3hvwFKe{n3ryBNj`WkL1o? zd{^qo%TeQ=_HC8WCO-YNoPi)1%ToKV+Uf^BoS7Q~tyo#NNTVHBJ+x}4j73LB9Yt3e z-+6j+94#@a4)3&@TFY89+NFTJSivUNx$8Rd%ykdy{AN?N3}rg^o6m{pN+I@U?Q}xJ zq<5Z~{I9-NDCNmLDxL7K#s_`#PR|m;0z|LlYM1#(5sOBLr2}DRI4j76S8h+IR#Kmc9E?z#67E79SwR(r&=@ACAg-{ zt?7DOTT^cH&>n3q8*p3E@q6VIeWT4GN!J&*KA*;>d8>lKRION6iRQS{0d3Rhv`YxQ z-E_06$poW$Mzty}73Gwjv2Uc#$uw0BP@E>|orBHzw|fn}&qP#k3aFjf+Si0l1C7?5 z%v&wxL3DB~*>T#gv4h>5Q>q$mOghZGV{!8h_mT<(%!D(doKKdV5#_q@wUuS>&8a2 zj>G(xa(hx&^-PPQHSrGr+e@8R%P?`75^fHKg)E?GwqDaL$;>`(q_1nu|CIg7KaG5O3}1dRo=)JU z3mGevqhB!Lc)w7Net|CI{X#kV1--`mg>ny#O;1;=!+h;X={B^j$rBev zDcgYc=%FFrlVBttw_o!ioyweiZMIn_Nqg6RoB(jNJm<@Kvi0DxtOfP-a zE>mAgd=tOdn(gE*1g^#}+tp@bWH)p&Q!_V}m0gIdR^L3Mn-ykL9*ibmNL3Fmu?c%` zwFaBPoNJxd*&ps_mFpkY4T{@}&F0d>N0yFjn=Oi-j)_mXIr-5-6Pqpc{2J~#Zev@9 z#fW~sf&1D297XV>XP48C40d7`SW3TK<^4i_pz$B6wEyla2Wl1fn%kom+nZS>Ob-n7 zzQ35HXgu`x@IY^q9x&J&BnE~DmMjm=%uG+MOjO2)$A`<>=m_NF@aV*}HaT7%ugnY& z>zY1R(WYm#>O>jo2xREW%;?C}NVz;VS=F>D<-F+;<^7r0A&tP+zRom0%hmWcuu8{S zAAMDCdRnDgWm6_o9D>CmSy^0#W2d`$w3BscHxR|F-q2hVn&n9AzE|e4D=j#{_AX24eQG! zhcGneWDXH1f=}NtlsRfZZln=oQK~_*yI+PQMGN;yX&JAzYHUPXshL8!dyQZmY&D9QeaZ4B%>gc#nK(fh+t1qTgs|UQd89i^vAu`I9IyQ|raqhPX zOT&tW1!@U8BG`}FzDBqj*@M?+={06~W3Mq1$f+ZfhzOgaOpD*r$do#*jwSbTHe0gK zQ!s!$pI2A>m01Qv!K#6w{78;UB?-zj1={oURR&xX1t-oL>URvc9wyW;-{T^?rfmBZ z($sYW#TOW1rw6Z#2wfFsxq^L)`nuQHWb+KWeS`>Z>o$+UF9Ef>qHFCA{4oZlVW0ON ztA_V&wSC-rP-)o?os0VafK_rlcr$MwjaH6t!Ohk#G=b(Yc}Gy{^_7}l!Xh~A3CZ#& z(j)fLTa4w*cDiuXNAM5a5h6X{^;;YWOb%xfzY+Fb0Zi5Z)~$9yi-pl7{{fi&T=RXl z#*c1Mr*DC!&qtAV2*<#ngmjGoQUC^&(^Z_GQ>|wpBFS|LS|e%z9!j2IqUs0*6P>kF zm)x?x0y!u;7=$((bkI~cp`iBd`SUd#w{5sC%&gDn@y>GfY}hchl@+7Tj<}K#)~ivK zJbCQR8|_1M85Q?3?I2+zk*Xk%+X&2gI8_i#ja2KZR<>Z{a}U(?YJ$D&-C(LDE1EjX zy~~KoF-0ZNG$*aRcUU zIV6@ITquEcB(&fvb~82`&~)6Y33Q0)P~~=S=YzMHjfGW9<`amV<~!SJse%1FS!3j| z@_WXa9li&{7Ttf&p`*ect76z;0&pENdnv=@4C97@VH-ZI7U|sJfb!2@^^B1t2EK1qu;Yb!L;TFqUj zeN1pY;wm+@v0lkb9KC*U-Mtz$hv5WU;oYt>5pzLw1$jK#MTWwPWRq7$(@@)(dAN=< ziq&&qOeERCTti@-pt2MprK@{nHc2!?k2vzbFO6W89?C{CTPAtK(V5juyfB}AU$352 zXF?`;eeY1`VN3=v<@s(oG(!&beR6j1xNZbk)bv5a6=q{8v&QCf6Dk5&#v65V@M6+D zf*0G8C6}#G1MoJ?%}{vL$>jm~4766OACrNO>JG=$GacQ+!sQS%BW?Y-dQOC`c&i<7 z%ZAzwovj>zCs-eFo`ZyA1W{Oj?)EsX6G=c*?65(;ZW6RvLEs?_02r^1g5*N>dgPq@F1(Jn@~ z9_5iPJ$3+#)+2KV7U?w&vdV;w?!@{i<1({u7QxO&Q8tWJlwr!pXUjC!Ygh!UDV&nr zi*Oi!fnlcrpP722wV_#S(_@Rd~5;B|MdQ;G;4NAb#1%^MfFtFUnuVnYB8jTXhKC0W>Ka2Egw;bu(^G1W= zcvEIgCO`roWD7=bXz3UiX06G@q|!nx>$>!*2TD>dAP zPeHY%ned>_!Ya8DR1qWyXA}u@_61zfxV!@6dQW(|4|brjt7i5fgMSrG9jZ;EayJ0KLYT z9zdSDTw8jjgF6B?)e3)eKyHk0{lS23JVEerH?{MAR@87|? zX|ufo2PNzN9sVXMvaB3z(729@9O9Mj8fv}|GS{@ppM*&w$@P#P4(aY^!SQ%tF1Dk} z%b;=unoC$O%K1!X;XSXzhd8pr`~`NA^>{*gBx?t11U@}lil>5dPsm3Svo7rn@1(Lg zI9{43yEg}1Um#|AnCyq&Z4$BgT@~5G&z)#W;-59PhyF1!?7PNUwhzFceahC07%8Jl zW^+N<1NzyjxKAwNzI(RX(|G7v>)KIK%F&U(CmkZF+mP~x zb4G;cnMY_)gTUXykN+QX$!t`pW9lkM|@FyjZn1P+?xf`nBRr&!@{W$<4yN*@pOewMO$>N z+gjg_bgs%^ZdSDRd#Avf*{Z+Y+Xd3HHQ&2;38ZApet7Q}NQ@wtyypdy6b>YR7cr}& zYL|E3=`Br1WJ$^EsQ28~FmKgo@1t954T z7;gH9n>d@C9FVktG7xzP^j&Dh9~D34>Kz7=%YeH`<5l-%x)xr{^ZQbVxoV;*1LzbXuA9&1&@w#&w(i9_IzZSwKcvwEMDV~d^Zd6f`Qqcz>)v@@y?<~# z*t31#fE>yghp$Z+lIR;)~>J(IZr0ett>*%5H2tY zTT=o-xT>kX*&J8LqogHlSS%SGv%(*Bhdp(OV&WnWuTe*^Xp0XI8U9SM5fRunEagRl z41b~$x3Z?8fK*(J$wj(&i{etkm)}x`f+7p4Wo{13cxa;ExG8v^`1so3@#5Zj^0Y7G z(l5e|Rzpl=7Vy6`Iy^c)JT*KqJUlWoK5}BA3D0NAeVb>Qrx3p%<(OWKSd`6Y*(olt&tc z1zZggus)otP0Go%t0EgCt!FV=5;Cy`5mHIvA_$5aV#(iqt0_ptcBaQeEbOqk>%JM% z&lV3^nmQ)Xxo*mbFbzM=VxMODbZlg=a73rYCJpiApF;@UbIIM7&)y3VMDrZJ`EA%SJ;mw zL~snhOJN@~$&2w7ZECbSF>iCL2Ssv9UHC?YN zhxtH_;TM@dqdL}q+KJ7k$NUd+`{yFQ`*hFHiy|;Axfp_3*%I;5qwYufkcuKcyXby4 z2&5?Dn;xk!h*Eq-X5AO@*~dImy^x6_KIyeo7#~r@M^8ke1M=|?!>_YQhU)0>WCn$7 zdUSMB*CwhfV^g>uOP^7WM-WsZ)8il?9vPpW)`v%@E83J+o*0`@^vH){748azn2-LM z%GmHowLCdJSsgA{M@AKGDKT!4il51+FT}!Pv}92)CPybHrm9opQ{ywkQxh{|O0_o% zY_8|PQk@>J&djWoheyUHCLjQnN^dMj7jhsO9~~R5%*>38PL;3kvf~m&~dLQ+h9x$j4c)~!zGc&*j5jiCf{I$L)3e0g3;3##%3K|I-0&& zM^7&aHkH`U-i~l7$hkN1ftv10ED_z+VV}A+ESRt>!-`}Mr{4%1BO(Eea2hFoRp%bs zpkbfCilZR-F~^lE3}-x)Q4j3F`i0SPQ#T#a$M=PgBPM#^ks#QpVIR98r8Mo!a#bGr zlD9uXMYu?hNkx3CJwug7nD@ERY|Tn@T_^J8@M^c)Qb>;=P5X@7!-xegerE(xV5?{3 zzz?eeM-XF$Zb0CiExV|`J}kyr%e!luH+QpV+$fk&vsbf%T$3c2s#p=Yg87st72$2t ztbDEumlFJmxLA)VccUed<2t1=LSE6EaIPfRlajs}TB);q*EKW8N~YS0S;-v3J|$X0 z;;`>B9y65Ouupn?O;~%peBBxT+|Rm8gJCp#akj%j@7&qnUx6_&oBi8KtxBliN9p(N ze%yq1REB&k`#3=Kji?t2~0(eJ+8$iT&sm<_$*d_bc6x;P>#73=dssvSB4} zidZgqn+tn~}b?sId&mEMYg zW)=T7gFA!YQmq1v*;^CJh0A(Y=s#i2+3CJ(bSovGJ%X6zMR^TSDKzw<`9%sJxB^s1h)m9QNlE;ssGE8?6k;A+@ zByZ^PEH@2wS7`TT_L|v)hV*B-X*iQa!Ka`t0 z%he<@nmhN*r&4kgUhDBVZ-wBXP`qQ{$J7ex@hmS5RV{%A{Fq8Zk7v1Q(35D;d(e>n zEH4eXkTl*F_%XE}dOXWbLsEahNt<*U(x2s}VI`?Q;Kx)NdOXWJNLOk*S855(f#3Hn zq*8KI@5i|-BuRR&q)X@thbO<% z!t+NLQz^Qs_v5@3b1G@D;>XmA>G3Qt4fXYe4uT(3Y3T7RZ{swqosDjp!8Z<`AABN} zqMLd@Ze|Ua(A8ra-6JPLQTiyk0WYRiUfxD(mJ=EYKc?Zip~thl(|xnNvuPxfvGdU3 zR7!5bYds$4u8<_fxY^@MCJD^mvw=hVD*^o$XR9y#wCR<5^xB%%om{ zA5-h0$FtltBuP@Uha~OsEH4e#sf5viA5-h0$FtltB=t$FN1yEREH@3^^DOmAtA`rg z<5}hl1J|O{y=CsKYwc!IgP1!H(37-QxdpHGERefek|tWK$3)xXS>B1Zy|%Nx)>SF| zw1(%)`%|mtrrwY9R!lpozu?ET7P_Iwv%EBPlEyB6Or@d6vs90e?rYh*`fTUS&dR1) z-`PpBisIoMJv{nkDosNf%4~r)8c(UT(TM3sTfZ`S+ipF{DuW+W+pfp6yvos;wVjnD zX@lSA4y96ZQ}4%lg&)4UhC4^Q#yLr0JlRiA(yHc`eg(EhQj)UbVocXqJd)B2WWKJv z5$pw5uy@8{hgda4!%4Y7DLr6TbaUpetOaEeTGhE@WD#eRE<&>wltn0v#^DBeFG8~x zltn0vrP&D0T2L0DFyv$-G;2XwgueG7AwPS6`Qr7EJC3MXCpLg!8gn50lrp|iQM}JDlM7jjPli^ zIb-R!Zn>VVHO*|r@1RWAL_D9_s6v2%eQ7-?vAi_8(-o6MI@AHnOQSnoPf4bG!1B`Q zPFGu!=^n7WG`iEZm}I&KEH91jbfqSl?g7h7qdQ&4Nv38@>%Mu zwxzL@_eJ?E_0{OoSjzjNe3tquduc4?eNjG3ef7XJmh!&%3F-4v7X2`VtKD08SAJXd z)gDv0n#%j4e3tqumT4^IeeqM$yI(eSGlj1Qw)A#U%=Obmdo!2w?N177xedcSW#M{L z=r=5|Y{@%>h=?UUZVd7ibM7pARH>cmtJJ2xS_ET#_0v@9X7ayU1Y>=*)O5!3zgh%i zeRb7z#`3>f1Y>f1Y>=**L246zgh%ieRbG$#`3@VMz`Y(q!p(q`ornUaLOAJsZZBmQ)n3;_zj8Frz@#x)DQfIMC#Ku(KPA@ zenTSl>FQ=0^#i{lk@|Fa zXRBh?aNBWnHOKLeFpy`|syULb$fdnf03Yf4SsGCzgTGP$AL*J}Dj$QtQUD+6I$J6q zgTGP$AL-g#Dj$QtQUD+6dR!_WgTGP$AL$xhDj$QtQUD+6x?L(CgTIpJ&hyBjK&$Wk z(No1sV`V(oTLrXAy1tjj%E;ia6u?Ki=9kLH;IC}i6QFZAUpF*BL-y}~$3RRsHR zH1K;L+NuV*5$ufTP$5$do1^JEWa>Lbu#&D4rjj+9p4=Kc)tMG0y?? z4R-0U#cqeh-DS0Q zw<>j0+fd;qs_G})pT;QAkUAL>Up9>mOE-7ouFyuQskLkCx@O_FHp{;)D|l1Yl!<`y zfnGDSM)k~2cD>f@oZhLKKF!nsA*Gg~+#QhGw`k@7Z8^xB9QP_UM&P}10@Dl2BoSfg z-FDAHVtV0@tVV9sT2^|^+}Bl&y-ROMBeG-Fdv!+~p!KAM+Z`Ky%5TahG`&W#pO{E$ z`Wtd<^>jMo#9Hl9K66C9K}UM4Rhb;G>uCdNXIGW>DK2faTVpfBr_+e+7Dzn{OB3lm zTCZEA_b4ilyy?lb`iqg5Ui3Nfqr0J1D!9kC4n2kBP|BF8ZepHP#)IxS$Xn0KB|5D& zU6QfH@Praia_@H}%t(gCl#ZUY8_q~sN?YP|Zu>NcQY96ACjwSB82U zT?2SWKwHmZnS)ynOjg*qQNr%(CM>O>q#EOzG)h>s#!`#PA}bVgJ;_QdmVK16C74>p zl7{mY>Ae>7u7rzTmChDn*b+-#zNNqNnat~aceSWmdZlC5+M8;{+-$XtRa0xNZK}8c zv8`9ydbJ99-DfT75tH?fynTI`%KDw`^OM^qXpkzFk+|!+tv9WjfvB~a+U2z!rLGOw3A ztNzM3_3Kpp;$;(VX0z5&ORp*ycz146rnauNaU=Mg(b_y0>rnk3(i*yQEpA!VO}$ys4|m$F4v?`AY$}%@ zHfyW3rdD5Q>y4$BUQsUaF7i}fg1S36zpqx;l`Hl(%6fI7+15>?Mb|!B%7yfRvdV?~ zYt8l%t-Xdb1%Nx<+2PjF_vf)GGdW zuUfDc8m)S*Qft>Y=k=9ZlWKpqUZaknyogbCB~|^=*0O<1ob`j6c}h2xm-;ovSbp&# z;Rb6=ztFafcSGOpGQq3I2iPFw9NmrHdq6 zELdyk!L^K4NJX!o1s}hz>p}Q|tGb@Bs_8Dy(Xn7Xs&Bf?D3=6n&pHxlv5fke?n_CUptz-qu+%^_F3kk_PdKJ$3DjVJ1FPkL%^s2ME##>3&jq zeVS6YHPiX270>%L2fC<1PU> zguh>j^i=@DMc?MX@euy@-@nYGnaUGwY3~qT{~f|P3)ll7-1L3IP0!E6-*ZUk0hc1b z7wJC0!+`yO1;8VKM*#-_2Lbf%A;4k45x`>rs?$ZJ^ghv*=Ns4K8`K`u7JNI<_m%+5 z0KYAcBA>NAlIs5$-aif?I;l<`N9xzZi}Cjp_?z%P384CVHGt}C8=g_yQcCYqT0%X9x7p{}+wB+G=i4v-!nO7V&u{yHec`Ob{kFhNN#uRJCNMr*moki)3NVDa+hPj0?8{J`)(w6 zJN7+D?s4pUk=*Op_aV8@v3DZb>DVd~)v7NQNDI1j&eFk0Kd$>@g%`jy;ZK z+_5K+OgQ!=l1ay&LNevp(@3Trdj`pjWA8??+p+IQa=&9gfaC$keh|rnj{Qm`uXOBJ zA$gT!KZN8V$DTzp>)3mc;Qw<3jy;cL-m&*0+3VQ*knD5phmkz&*!z*}ckBfu3y%E= zl1Ci-Q6!H#_5maZ9Qz=WgN}U&$sxx+jO4ImA3<`&u^&V7m}4&@S#<0rBukFHjAYrd zk0Lqh*vF6@bL`_tjyv|_NFI0WCy+eh*iRyP(y?ES9a~4DJN62a6~|shvg+7tNY)&?hNR}$Cy|_V z>{CciId&aM-LV@;8jjsW(sXPCiQ(8SBrV5&2FWvyZ6Yxp+d^VFb{k3Cu{%gQj=hd# z-LW^2Y&iC5B&QvF6UnAypFwiQv7bfqtYg0x$!i_^IV8_H_Un+m&aq#Q^CBLqhr4b$(tPe%}Czt*l$7d7RP=olD9hc+mO7?vEPp5?T-BpB=2zScOrSGW4{Z@ zyBz!7NZ#$(??Lh&$9^x8_d53bki5^azYNKjIrjUJyx*}ufaC*?{XCNA9s7ewKIqsV zLh>QU{xFgcJN8GAe8jOoisYk?{V^mTbL@{J`M6_$0?8*F`;$mM>DZq_@+rsuG?Gs{ z_Ln31a>xD*lFvByXOVo?u|J38bB_HLNWQ|cKab?|j{TKLzS6P33dvVF_E#hMYRCQ> zBwyp$UyI~x9sBE$e4S%|J(90?>~BEw4UYYdNWRgrzX{1VIrcXr`DVxd79`)|*x!oe zTOIq`kbIkCe>;+IckJ&#@*R%-ok+gZvA+w+cRBWVBl&K}{vIUX`EX~+H|a3g3y%GZ zNPf|=e+kJiIrc9j`DMrc6(qmn*uRS8R~`G;ko=ls|2mRickJIl@*9r*n@E1sv40E6 zZ#njFBl&H|{v9O0)8K_}zajZI$NqOD|L)lTf#g3N`#+KVr(=Hs$rl{^zmWWwWB)gj|90&E zLGnM2{Y4~SbnGu7`I2M*FOvUt?EgdZf6iHD+aCJIIlGNA+mJbrGUp+)oif{zc@bq^ zgv|MrIUku9Q|86UTtJx%khzdD7b5c#%De=bizssuG8a?kVq{)QnU^AS31u!p=2FUB zip*t{xeS@hDRVh8S5W2(WUi#lmB?I0nX8bwnle`-a}8y#LFQ$Yc^NX-Qs!D@uA|I# z$XrjE>yf#EGB+Uea>~3MnHwo{BQiHp<|brrrp(RA+(MaKkl8_*9mw2DnOl(=qRbF7 zw^8OcWNxR-?a16gnLCiVlQMTAa~EaqLgp2ec?B|eQ|4}D?xD;*$lObrdy%=1GWQ{~ zlQKJzQ7NM$vx_pjkQt`TFft>Q89`>0GNZ_hQDzL8amtJ%GeMaNWF{#yiOdvbrjVJY z%rr7Hl$k+hH)VDsb3bM7N9F;_Jb=uDlz9-DS5oGc$h?X&uR`V_$~=V3EM;bq*+ZE< z$jnh@4w-q%%pbid;O_(M?}NeLhuGhT zgTIfkzaI#fHwi&40sFRt$?=y-VS&N;GKYX0p1OG58%Ck_W`~P z@P5Dt0M7$H2>1}-!+?(fJ_`64;NyT#06q!$6yVcbE1o&mZuK<1(@N0ly z2mA)$Hvzu|_-(-N0Dc$ndw|~u`~lz(0e=MeW5AyP{uJi3Me+Bq!z~2D= z7Vvj~zX$vS;2#111o&sbzW{8&S-`&n9KgQ;{vGfifd2%10q|dd{|5XI;ERAS0sa^8 ze*opYht6&ToCnwrcoE=yz>5JF04@Z)1aJ}HV!%rQmjEsWTn4xta0TE>z*T^&0oMRt z2Dlb*9pHMv4S<&eZUo!}xEXK@UYz3*bTTJ@BrXJz$*c-0z3qm1?&OL0p}}a(_p;zoXpWQ||9EnfwD~{sEbPq|84e^G}rdCuIJaGXIRszfk61kg+L4 zQ_xw;oJHneDf6$$IFxaa`8Ue^8#4b+nSV#-KPdAb$owZ|{u7xmQ05EB{1;{Z3z`3> z%zq>EKa}|&WWGq5FCz0L;_S+^|9jp(Ows?x{#CY9ejEGuJofK)_V0_>zvr`mU(Ei! zfc<+R`}Za6-;3D47qfp~%Kp8C{d+0?J$u>qvzN2KuGs$kw&%}Yx&2|l*{inGzgKTR zd(HN1(=kD#W`<%T8S^S4=@Gt(l(l2!H zc36diLRYdv|F6CCfU~05wmw=NhNNVWoDs=cg5-=aiW!t-20;Y_AQ_aLbCwJWNX|Jb z2F#cdbM|bdE@3JvbVbFPT+HMarjkNGRh-GqOdeq> zD|BMTnLNx~AWRj79I)o6%biNXTaQs}RX zZ>TUcMTDuX5=DO7P!YWv4bdx6RG>O4QS>*cXaq%H0T&bK5`~Vy_}+@~hKdVQSD}|M z&J<^+gfR6Kx)9?`31&(PQ(vKfG0v1^rj#%Z6gnT{OetnA6sDm<&t#mrkeSlLG*alc zj5DQ~xk#AC3VoV!<|1aw2-8HN!!yp5VWzAwO%-}W<4jp*$_djq;#+jc z(9;}eE@q~tFjpvaL&uq#%+wO*N`*e^I8%$6+QM9=(19IiYBN(un5z|fx8qD5W-bxt z8ilU!ICBXzb%nWBp(%h0IHwxq_K1h3TP?mWeZ0 zGINzMcPiv;;>=adTrJF93JIM!b2T&92y?eWwkOV9!_2k9^i)U%#hGiFxlWi~3VEV9 za~(77gtp*9&u>Li#DrT+hr6!rZTrn~F0xFw;Sp-U^AU zIMacdj>7a&$ZExzj?8otrmsTEE6#Lcrn4{)s6=On>C8+QVfv{=7l-M>%#FhISIDHr z_jMyPHwiO9A8C)mzf8InWm5ejx!H1 z(@&V`3i;tU(~p_{!pu-e9>p&J18?pfIx((#~;aATxu6nXQnMjx&Rp z87#~kg@kpS8O+QOVdkpD5QiDU%ur$GDWtmN+ZoEtFk$8^H{T$qIl>Ge1>oS6~AEK&08=B6{3}MzQ6jQ`^HiMa& z!fa5ew6HsysaK;RdL?EFv{9iBBfgDHN92)3iXZMnm*UEEH(J zN-X>hS{OmmSHOz|I-uyFySqiarNsgrQgqmb7BjR&pvM(G;X+FoS}M>HMNhiWQihfZ z^pv8fU1%9Y%LRHy(NPy#&d>^ho>lam3$0-25rLjp^nwdL!qB4vy{PCV7kZSTl>)u2 z=$H$wWN4K@uPA!eg;p`NTAOz|s z+APp#iavLt%?xc3=nF+(y3iJewhEL`D3prddA2gMO&C)2mAj#B3~d)EP0`mbw4I?H z0)3#~v|FH`6rFUT-3;v!=x0U0xX>Pk_6qc? zqI8~fp7t`dPoQ%=RE^nt*gm})4bdyHU!Ze6Qtp=aGju>8&m-T34ls03punThg$^=w zNT3vt(!0rp-zI?B+q0_FFp zfD1j#&~pM6^r(;vJ;%`V0u}bChzmW>&{9s(Mt--O`&3y(Lg}k7~HkTMQi+ z=wgp*y3lck-WI5qN3~t(ZH7(=RL7%BT<8Qt?+8@aqk1m%4nyw>RNtcpF7z%#?+Mh< zqed?D9z*X7)YzjYF7!S_9|+Xcqh>Dj0Ye`O)ZC*MF7zQo9|_dbqgF2T5knsfbg4(J zUFc(mJ`t#mN2xCK2}7R>beTt&yU?c$eI`&_kFIc`&lvh#pesGP%7s2>=nH|a_UIZH z`huY^1-jOw>s;tdh7tm`^QgTGB^V-suJ`B$7b1qf5~zbm9bM=vhSCJ;Zu0167y5>wZw0!=qg!3*TZX<9=r)gTccJeX`d*-}9(8k}?-}|* zpgTP3?m|B>^rJvMJi60`eq`t;f$s9?ZWsEAp_2mj^r)8$on+`|f$s6>UKjeAp;DUX&dZZiw*E-po^i3 z+hEFKgFG7SVyN*pm}jve9u0LdRDc`Ix7aX`9&|C(hZ`)g*h3x-cQI6t8!WWg2#-d( z7;4K6mSV9{9*uS}RGu3wy~W0OG}gsXmu|2O78~c$co##}y1_D9Y=TD0E6YW*m93nxERI`7%Y#)9`Wc=7sJQ`gI!>;l^(5fF^oGfSYC^* z_UJJe!>9y<<+IotkJh>v#w-{tzs1&hwBE%qqQPJVEVcn3tHe9&K{B zhS3lPD`c_F9&K?kjFm80VT*0`Xq$^+B!$6>SZupTJ6sIoEDToEVmm$BfUXS*<7)Edytc1n(dvw6XFuucJB`tQ)qeCu+(H{mYWwFB^J?>%{ z8)C2vE%t;*M_ddeM+{clVo!SXl#5|piNP+i*wY?8<6;6IW@DW8w^(7VlR61lDjpGb}?85i@og8F&D#F7=u-`*ef2r z>S7ovW3WmVd(ETQU96H|l`Zy$M{l}VWx=Xg>@AOuyI2*$s#@%Ak50H4M(CJht7frx zJbKr~Fn-5i)h+g(NAJ5BM)w%3hQ&Vc=tCF7*dK#kY_X3#`q;%VGRR;xE%u2=pSl>v z4H>MK#Xj@sa~H#?B7@bo*cTps>0%gjWUx9GOL#;sh7m~yyToE&d6ed27_Vfox)%G| zqi|z*2 zWw1sT`^BSQT?}Ka4A$6U>3llJcW(F?fn~6BeH2?4BjvXw&qrA@`91}9A@nK5E~NJ< zgI&nzQzpBR*{AdD!udXBu?ty!%4Qd``;@~jp!89r3LEv;g`*YlMf^loZZ7I4vav1ZC-Sl_?k5VcE#W5$u`TH*im)x^CyKJY z&`&gATiQ=dV|$UGn8~(`Pi1A#<^03~ZZ7X97P772Q$-;v`H45Uxw21Hq`9h3)x=l# zsfPHAeX1$GmQS_uvzkD4@O2Vh;$wgcR1Zs2slML|b?j*vuc%t#=SmIyC^R&okqM2X zZ*1bDKCFpPO>s#y)90?aZfNdP3u$N>?WPset?5!9t5Go48Z9TYpf0}+gu?tb5T#RX zAe82}f#_U>a}_BBrN~3@6a@%@q7Wfel!B0=C_O@YMHvt>D9VVCQBfv@Oo}oiWX6?< z;45kq9ZqWW^)h^&O_%$r)Yhjf5U%v;DukwUTbp@UBy5jy$Q z8KH|$HzM5R)6EFC_=5&@NJ$sHek^jz^Bw3`KYGAJPP6;y!40H;S0*J=_y>5KPq)ht zr>p#M%wg)m1QV&7j{yRS6hs1xuNWV2hp)#6boZ$T&NiBgpE5o`kMBGA>}d_})5Few zZ`hWHAtOE%Kp~U@rH3*=8KF#2X6QWVd?*W)70L!>hjKtUpLjiDw`Q>Yo#9BKizgjzwDLam`TP%3m8bUD-(x&pcqx(d1)x(2!y zx(;dwwTG^UZh$&K9idK8XQ&HwBXkpVGjt1dD|8!lJJc2G2HgR5hk8JFLU%!TLp`Bh z&^^$-(0$PTP;aOY)E9aH>Ie0Q210|N!O#$BC^QUu5PAq24vm0DLZhJ3&=_beG#;7& zO@t;vlc6clRA?GB9hw2ngl0jrp*hf8XdW~ldKg*&Erb?9i=idZQfL{p99jY0iM!NY z(B04~EU$(hgVsPj(Y79O6SNuH25pCSKs%vb&~B&~xO<>`q5Giwq25p*s4sK^%maw; zBI;o_Cl@8UN}tKFe^J@?_pS>_X7dxtYoJ6+q!+_`f?lDTu|g#6juc`_+( zaz)9cxN{->*`#EQ_-U9U-W%EUjGtJ%rR(C$CPx@ z>!zzutpXW{Di9jvpZ+`_J${?#g9iHz)9@vYhG;Xy$1@snkIs%qG$Q2qBXfPwQ2m`l z@tyhbE%{`&4;rSMhoQM3n(^qt2leuUST2lZJVEdwz5EcCi($DKCi*}q{I@IHPSk>(|yngy^j&tM=Ca+>dgB=BlX&mSlbqB+n)UsfY2y?z@u;-j@D*0 z%ouIPz>L*qEX+7<#=(r&W<1OUZ6?4>)Mg^gByA?aOx9*H%oJ^=z)aOG!&0K7y8%}Yzf5W6DG*9nw9`@K5 zd+ht)J$nhw*KcM%-pq8o89RRoJ*=A_M)NE*mSkP5v-5RjY5y==0~x9 zr8X;JR%x>eX0JIF6r#3rbc4@N zfIbrk@QY(-dO4_D4x;5>O!;z1Gl#(ZljnXptWUyWoP@u25}0Z9xZemh>rePm=OEmX zXc-f&(33tr6*be-(q%4}KM{n&Y*1X}x}M@?!|6})avU|VZ$tN>9{gVDKInd^H`E8}3!T9Feu({{ci;yizKf`*czF-; zKQ+C}``GvCOz-jmzVFm2UOvR~Up>vsN7%>5&?nHR&}Y!+&==5`Py!<8D<}>68qziT z-ynVqeFuFH{Q&(4{RI7Arj0p?@BepC9rH8xDO1P%qTd7F->-=2g5Sm>oCAL@q#zIS zp#YMpWkUEAC>`py&wV#7APx}4ayGXfO0~)pxjU%$ip^#=mPiv zF@#c}^iT#UBa{is44ns^4`qS!g3kx#hYCOip+Znrtjh*vhjKtUp91~Fl@3kY2trOH4`ge_sD2n~+u@S`(_4tSZ&_Jj-mIomYhW_k0iE;Rz(-|vK z0{cAO@e(ERy``X)*ta`gVlCP(#5z4@Vgs}hlCcw|(S8wB2Kv2m6nnt$h0222hgc4A zKjH!CAan>i3_T7#0Ud$LgQ);jgepOmp(;>Ss2WrqssUXL)r5}Ydukz`K&*|Z$7Gx` zWli2cJY`Ltpb4g&3ozT9HkZKE)ut}YX(dMfm$_@|=?&Jy2JIv^^>s^qw4BM=Y$j5J zplLMOjG1PpVbF{k2Gl6fUok4IaiFJ}X%bLV>=;d_PBYUi(9_H`52#r{EuydZhL(XI zM%F5zOYsd@aq2L#XquVs=b)!is<~SSQMV&cG^0IU9AJi6uuY)5CFR*o+H24Rd>+a! z8!Y>en{Ec3uJY!qD%}GcdoY)7h+b!E!Wnet%I<0?-Lo6t9U9E;3JUf6Q_i3tSfH91 z3FU4I4Q5k?h5G$TXV8T#P;I6AljBE)2D7PRLM3OEIrXUrm|@18pE7cOk~7N0&QE1N zKUL)XBxjU~ou68Kern74`FqVMgEWr3vGz*mbmF%cG?-tXhC(Ihl!;v)E%^Mjl=Ji7 zGp7u)YjV)3fljsA&mi!4>89wFNEPm{pL_;MJ=xi1fzI&RgM!xwWdc4JwG}QoOHJ&G zypDfp?d1F*Psb7Q!Lm%h%)~Iq6$ERCcsR!;PpXN86Y641T<&@%rpz$ z5a`MQd((y2pb5C?{*ebfpwvJvhz>zicObh(4T9d92{1D(*fEIe5(Gqzf(A{1nQ6gJ zK~%ROAZi#iXadYE3w92ox&{GJ89v~;x_?r z4Rj@(z4yUu&;t^%~@4_<>N z;QY;%H~oK|P76havZ)?HRADH`w!-Yv1nm0}3*H&%3PgKs@cP(Hz_FdN3A#|wD93hJ z5LGwIv7ypY@6rUAl@`1^h^ik2L?xsKO@LWt!Ja`>9Vs9xB{gUQ%xVkv3Zm*s0Z~b* zK@(sev*0~JR9z_`DlIi=0?Zl<-Wx>KmjbRapa}riTJXLgs?HP;m702sCcvz-;Qc{V zy(!>21DXJEy#;#*QFW()sPxoZGy!IV1^Wb1^{0TS1l6DkFdHq{H;Af31w^H&22Ft3 zWWfi5sCraDRFZ1Y1enbh>=#7Ur2?YTRD&kKY_VYfAgVqU5S6GJGy!I-1qTFCb*g}< zRMnsfFxxCRFwoVj_R5CWpb5CL&)9?EPz)s^|FT`mzWIUgLIJ@dh# z>{xy-gM+9BS*|VABkP}^2{1b?I3$Q_lm$e+vIb3n*=50@K~%#mAnKVlXadY`3l0mS z8fO7f@2o)+VD?z>!62%E77+E&8Z-fBuLU0pbd9vV7~wT&0xrhnEO?3JEO=+(EO@A> zmtV)>fiADNS1`Q3f=$2`oO}ph@*#X_fAfd%{kP79mwZrP@qT6X_TA|&*_!TDAUU%XT#IKEoqdT4bQnk{_Jdc z2kiZ5WDt$#lum#H=6+-X&h#IgHt(R_-l!lN4Jz9^Xtrkpw)Y>IU+<7Ty3s*24pols zkU2UNaCFIu_{{HDaw0zG_bWLOpL0Tz6Y)7G#7@L_*xqc$1kreJx!D{xHyaahvq{d_ zXKprU!Hj($(bz!GL&h`qjk61wv2VOxm=Iv#7*AJ*v11d(PtxPOxP7v&w`M;@mpQYa zsw*woPebjTeD4fYy@{WtOCp(>txFHt&(Rf-?C0ttNA~k{9V7esx&o2?!@9_j{Q_M_ z$bO+N6=c6iR|B$NtZVt$FVW?C?3e0_Jod|UEgk#ix&V*;3M75y_#f3}blkpDSIV(p zr3;?euSPah_%*s1fZNyV+Cn_>^b|*Izi5Ui_ToL zFXtl@&3>!S8nfS~6T|Gc>)bE<9Xh?sey7ghvfrhXwCs25JS_V?I@QX4ug<2j-=`C% z?Dy-ODEk9C?aBV2&TO(jq?4EI59|CS`^R+(lKm3_9g%bRq|P&O`%^lV#QtfWEn@$S zP6)9-s&hW zAqC>YkW$2_4=IEAj3H$bpE;!S#GfBh7V%j_$|gR0NIAsk3@MlR+#%%=e?drj#peqt zzxVc z@l`^qD!y7s)y3Bc>0JbAvG1>ETrb* zTZGh7e5;Ty72i6fHsVu5x=j4#A+;5MMMzhQzbd4w#a|QBwc@V}sh#-tAzd&2hLAdl z?-)`i@ts5JBL2pZZW4cUNVkZ;HKg0b-yTv|@!dkYLwxs;dWgR>q`So59a2y6y+XQ2 z{JkOFC;tABdW-K9QeW{8gw#)b|BwcV9~jag@qf zA05&d@nb_8Cw_cL6U0vpX_ENKAx#lKHKb|cr-w8{{LGMMiJu+P9Px8QnkRmKNDqr& z5Yj^Ni$Yo~eo076#V-qKx%d?!JtF?mkXDLc71C<)kA<{G{MwM#iC-Vm2Jst1+9ZB+ zNL$2j4QZSB?IGa7d4fehV+{F*F$(mUec z4e34c?}zk(_zy$+Nc_hkeIowTkUkUtc}QP~|1zY6cnaw&@o6D_E&iL3z7_vnNZ*V9 zA*3J0{}j?m@jr+3i}+tdN+(m;o|8i7idQM*iT6_|5Fe&cium*?ltFyP6v`w%a|)d& z{`?fmB0g&hWfPx0g>s0`nL@e5=T4zK;x9;{yyEkv{O)|mQ}C%adR%vY^VIGFCKNQ` zOiXh;;V++c_{*@V?iZ|(5`~H;R5IaN%wReR&xL52rXLRRoJynTRU(tVg{9#O4Gg*A zD7Xofo#|%!84?4+W@)&;MH4k23sWh&x>Iq5)2mpDuJ2S_3u9k7kMf!bo_3lLeT?v6It~k<;Ee+raY=S zPfM-?|MJT55z>4NnLQzSCQVM^>G^f%EDTd}$R-wMPIh9vb0imLzN(fcd Hph5o+)rRzy literal 248823 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 delta 10 RcmdnVF_DFlc_X6$695k`0v`YX diff --git a/.vs/Basestation_Software/v17/fileList.bin b/.vs/Basestation_Software/v17/fileList.bin index 7b52677fadb5659671f086d235f6c8c485173baf..0b7305650e88052eed1968c3fe14d52471bebc90 100644 GIT binary patch literal 347965 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 Me{0(~ueuKV|98pQG5`Po literal 327366 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%0Ad=FS!FkP} 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
E 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 wY9#RI#YXEPdL3-B5!I{58Vvc_j7~&^U>_b4*@~ literal 0 HcmV?d00001 diff --git a/.vs/ProjectEvaluation/basestation_software.metadata.v9.bin b/.vs/ProjectEvaluation/basestation_software.metadata.v9.bin deleted file mode 100644 index 08473c4b8fc5fc9ce9677cbb52638eac4013cc64..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2257 zcmbVN4NR6*6uvJ9CB1! z{#(wZj`J4bODxSVW7b@#CEfeI}}1pCS$jSE`%>kRhPsyqB5Vr zt9~68F^$}|y{=nb2S?U(x*i#ESMy>6<{LR}KetAki)K3AX3Zd+bsQR^p z<(erE7piud_o*g3!B6*yGbw!?7TM=X?B1py!{cH531VSI?MaF7nL(v|oV%q6p&X<4 zlI+oc0lS+x4V;{#-6Z}Z*4eit8HZ)GLf4nujrAIr${2+f`Rbv|+gM+J)_;~S41bBZ zJ%|-11e9dM_A`{;IahaYGmJ=RQcIBdU^!8}@hhlqgZ-1VwQHXCR-%zYy+d{7?bzJ` zqyg!`PEHMvRsW>f?ssA`gA)%+zNQh;MC9M>dP$Qrp_>KVcVeQ7qMCa3a0X04pHrB) zNUU|sqPDSkvuya#N@HUy^uU(E%M-MCf^ELazs$j1y8xRjv6TgBb)&(>AH)KW#QAyQ z@NQ1|R%^ND)*epvl~I+dEN)Quy2WFxrUf-=$@e@e?4!-PGfZ!vDJURT9b!kPgC!JJukkas2sZw8N@zM z9$lweJ47p&+1<%9Ok^6D?It$$h0KHMWwrG3s3JXzQYw4XF6I+^WaIgXrBEm#?;yJg z8(CM|70%KANWCIO2Q{Xw&`WNQac>Rnv)I%g$*ew;YmROkB(PunajzX;clFKKFHPFnILnKy?H=BumG=6fN=@%HBbl? z0dE8E0L8#wU=H+b7#Cuk4+H}HFfRduAp2q550nCBKsitW8~{Q%v&w^9<|*=HQ`v2LK4IFf~eoq>mR(y}$tnE}Ymj+iS45!msHgO8u{FYuwjhvoyvC9qIaY7#F pba?)|rnh)FyUp)#8L>Z{)eo>XQf3S6nctxwb5nM^{rAjP{sCPNF-iac diff --git a/.vs/ProjectEvaluation/basestation_software.projects.v7.bin b/.vs/ProjectEvaluation/basestation_software.projects.v7.bin new file mode 100644 index 0000000000000000000000000000000000000000..de1f4107718b808d8fecf081a0670f13e9232e79 GIT binary patch literal 2180809 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 z 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 _ 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 X(x*00Zxs5AS=0{(H?GHZD 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 literal 0 HcmV?d00001 diff --git a/.vs/ProjectEvaluation/basestation_software.projects.v9.bin b/.vs/ProjectEvaluation/basestation_software.projects.v9.bin deleted file mode 100644 index e3cd6c472850e3948bc9ce6c8404104cee6e4021..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 306045 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 + + @@ -17,6 +19,8 @@ + + diff --git a/Basestation_Software.Web/Core/Components/RoverMap.razor b/Basestation_Software.Web/Core/Components/RoverMap.razor index 5572e72..d05eee2 100644 --- a/Basestation_Software.Web/Core/Components/RoverMap.razor +++ b/Basestation_Software.Web/Core/Components/RoverMap.razor @@ -2,6 +2,65 @@ @inject MapTileService _MapTileService @inject GPSWaypointService _GPSWaypointService @inject NavigationManager _NavigationManager +@inject IJSRuntime _IJSRuntime + + -

+

Map

@@ -94,7 +94,7 @@ } else { -
+
}
diff --git a/Basestation_Software.Web/Core/Pages/RED.razor b/Basestation_Software.Web/Core/Pages/RED.razor index 98650bf..3b4cb63 100644 --- a/Basestation_Software.Web/Core/Pages/RED.razor +++ b/Basestation_Software.Web/Core/Pages/RED.razor @@ -10,7 +10,7 @@
-
+
@@ -27,7 +27,7 @@
-
+
From c1e350e81793af8d3106d38240155de46513d9bc Mon Sep 17 00:00:00 2001 From: mjjm4d Date: Sun, 15 Sep 2024 01:48:00 -0500 Subject: [PATCH 05/15] Header and other style fixes --- .../Core/Components/PingGraph.razor | 2 +- .../Core/Components/RoverMap.razor | 6 +++--- .../Core/Components/TaskTimers.razor | 4 +--- .../Core/Components/Waypoints.razor | 8 ++++---- Basestation_Software.Web/Core/Pages/RED.razor | 4 ++-- Basestation_Software.Web/wwwroot/bootstrap-ext.css | 13 +++++++++++++ 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Basestation_Software.Web/Core/Components/PingGraph.razor b/Basestation_Software.Web/Core/Components/PingGraph.razor index 8d07fe8..cd9914f 100644 --- a/Basestation_Software.Web/Core/Components/PingGraph.razor +++ b/Basestation_Software.Web/Core/Components/PingGraph.razor @@ -10,7 +10,7 @@
-
Ping Graph
+

Ping Graph

@if (!_dataLoaded) diff --git a/Basestation_Software.Web/Core/Components/RoverMap.razor b/Basestation_Software.Web/Core/Components/RoverMap.razor index d05eee2..118df3e 100644 --- a/Basestation_Software.Web/Core/Components/RoverMap.razor +++ b/Basestation_Software.Web/Core/Components/RoverMap.razor @@ -70,10 +70,10 @@ } -
+
-
Map
+

Map

@if (_cachingMapTiles) {
@@ -94,7 +94,7 @@ } else { -
+
}
diff --git a/Basestation_Software.Web/Core/Components/TaskTimers.razor b/Basestation_Software.Web/Core/Components/TaskTimers.razor index 4f9788d..42d311f 100644 --- a/Basestation_Software.Web/Core/Components/TaskTimers.razor +++ b/Basestation_Software.Web/Core/Components/TaskTimers.razor @@ -33,9 +33,7 @@
-
-

Task Timer

-
+

Task Timer

diff --git a/Basestation_Software.Web/Core/Components/Waypoints.razor b/Basestation_Software.Web/Core/Components/Waypoints.razor index eb83917..bec7c5b 100644 --- a/Basestation_Software.Web/Core/Components/Waypoints.razor +++ b/Basestation_Software.Web/Core/Components/Waypoints.razor @@ -5,14 +5,14 @@
-
Waypoints
-
 
- +

Waypoints

+
 
+
@if (_waypointsToUpdate.Count > 0 || _waypointsToInsert.Count > 0) { -
 
+
 
} diff --git a/Basestation_Software.Web/Core/Pages/RED.razor b/Basestation_Software.Web/Core/Pages/RED.razor index 98650bf..3b4cb63 100644 --- a/Basestation_Software.Web/Core/Pages/RED.razor +++ b/Basestation_Software.Web/Core/Pages/RED.razor @@ -10,7 +10,7 @@
-
+
@@ -27,7 +27,7 @@
-
+
diff --git a/Basestation_Software.Web/wwwroot/bootstrap-ext.css b/Basestation_Software.Web/wwwroot/bootstrap-ext.css index f255a54..2050338 100644 --- a/Basestation_Software.Web/wwwroot/bootstrap-ext.css +++ b/Basestation_Software.Web/wwwroot/bootstrap-ext.css @@ -3,6 +3,19 @@ color: white; } +.card-header h3 { + font-size: larger; + margin-block: auto; + height: 100%; + vertical-align: middle; +} + +.card-header .vr { + width: 1px; + margin-inline: 10px; + opacity: 100%; +} + .card .read-more:hover { font-size: 14px; } From 22cad7296b7b39a630b9b6eebb3f53df75f3469b Mon Sep 17 00:00:00 2001 From: Adam Klassen Date: Sat, 21 Sep 2024 03:48:06 +0000 Subject: [PATCH 06/15] finished rovecomm first prototype --- .devcontainer/devcontainer.json | 21 +- .../RoveComm/RoveCommManifest.cs | 1034 +++++++++++++++++ .../RoveComm/RoveCommPacket.cs | 45 + .../RoveComm/RoveCommUtils.cs | 400 +++++++ .../Core/Components/RoveCommExample.razor | 91 ++ Basestation_Software.Web/Core/Pages/RON.razor | 3 + .../Core/Services/RoveComm/RoveCommService.cs | 404 +++++++ .../Core/Services/RoveComm/RoveCommTCP.cs | 460 ++++++++ .../Core/Services/RoveComm/RoveCommUDP.cs | 354 ++++++ Basestation_Software.Web/Core/_Imports.razor | 5 +- Basestation_Software.Web/Program.cs | 3 + 11 files changed, 2818 insertions(+), 2 deletions(-) create mode 100644 Basestation_Software.Models/RoveComm/RoveCommManifest.cs create mode 100644 Basestation_Software.Models/RoveComm/RoveCommPacket.cs create mode 100644 Basestation_Software.Models/RoveComm/RoveCommUtils.cs create mode 100644 Basestation_Software.Web/Core/Components/RoveCommExample.razor create mode 100644 Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs create mode 100644 Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs create mode 100644 Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 2be2d74..80075d9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -41,7 +41,26 @@ // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], + "forwardPorts": [ + 11000, + 12000 + ], + + "portsAttributes": { + "11000": { + "label": "RoveComm UDP", + "onAutoForward": "notify" + }, + "12000": { + "label": "RoveComm TCP", + "onAutoForward": "notify" + } + }, + + "appPort": [ + "11000:11000/udp", + "12000:12000/tcp" + ], // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "dotnet tool install --global dotnet-ef --verbosity diagnostic && dotnet tool install --global dotnet-aspnet-codegenerator --verbosity diagnostic", diff --git a/Basestation_Software.Models/RoveComm/RoveCommManifest.cs b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs new file mode 100644 index 0000000..abc2f33 --- /dev/null +++ b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs @@ -0,0 +1,1034 @@ +namespace Basestation_Software.Models.RoveComm; + +public static class RoveCommConsts +{ + public static readonly int RoveCommVersion = 3; + public static readonly int UDPPort = 11000; + public static readonly int TCPPort = 12000; + public static readonly int HeaderSize = 6; + public static readonly int MaxDataSize = 65535 / 3; +} + +public enum RoveCommDataType +{ + INT8_T = 0, + UINT8_T = 1, + INT16_T = 2, + UINT16_T = 3, + INT32_T = 4, + UINT32_T = 5, + FLOAT = 6, + DOUBLE = 7, + CHAR = 8, +} + +public class RoveCommBoardDesc +{ + public string IP { get; init; } + public IReadOnlyDictionary Commands { get; init; } + public IReadOnlyDictionary Telemetry { get; init; } + public IReadOnlyDictionary Errors { get; init; } + + public RoveCommBoardDesc(string ip, + IReadOnlyDictionary? commands = null, + IReadOnlyDictionary? telemetry = null, + IReadOnlyDictionary? errors = null) + { + IP = ip; + Commands = commands ?? new Dictionary(); + Telemetry = telemetry ?? new Dictionary(); + Errors = errors ?? new Dictionary(); + } +} + + +public class RoveCommPacketDesc +{ + public int DataID { get; init; } + public int DataCount { get; init; } + public RoveCommDataType DataType { get; init; } + + public RoveCommPacketDesc(int dataId, int dataCount, RoveCommDataType dataType) + { + DataID = dataId; + DataCount = dataCount; + DataType = dataType; + } +} + +public static class RoveCommManifest +{ + public static class SystemPackets + { + public static readonly int PING = 1; + public static readonly int PING_REPLY = 2; + public static readonly int SUBSCRIBE = 3; + public static readonly int UNSUBSCRIBE = 4; + public static readonly int INVALID_VERSION = 5; + public static readonly int NO_DATA = 6; + } + + public static readonly IReadOnlyDictionary Boards = new Dictionary + { + ["Core"] = new RoveCommBoardDesc + ( + ip: "192.168.2.110", + commands: new Dictionary + { + // [LeftSpeed, RightSpeed] (-1, 1)-> (-100%, 100%) + ["DriveLeftRight"] = new RoveCommPacketDesc + ( + 3000, + 2, + RoveCommDataType.FLOAT + ), + // [LF, LM, LR, RF, RM, RR] (-1, 1)-> (-100%, 100%) + ["DriveIndividual"] = new RoveCommPacketDesc + ( + 3001, + 6, + RoveCommDataType.FLOAT + ), + // [0-override off, 1-override on] + ["WatchdogOverride"] = new RoveCommPacketDesc + ( + 3002, + 1, + RoveCommDataType.UINT8_T + ), + // [Tilt](degrees -180-180) + ["LeftDriveGimbalIncrement"] = new RoveCommPacketDesc + ( + 3003, + 1, + RoveCommDataType.INT16_T + ), + // [Tilt](degrees -180-180) + ["RightDriveGimbalIncrement"] = new RoveCommPacketDesc + ( + 3004, + 1, + RoveCommDataType.INT16_T + ), + // [Pan, Tilt](degrees -180-180) + ["LeftMainGimbalIncrement"] = new RoveCommPacketDesc + ( + 3005, + 2, + RoveCommDataType.INT16_T + ), + // [Pan, Tilt](degrees -180-180) + ["RightMainGimbalIncrement"] = new RoveCommPacketDesc + ( + 3006, + 2, + RoveCommDataType.INT16_T + ), + // [Tilt](degrees -180-180) + ["BackDriveGimbalIncrement"] = new RoveCommPacketDesc + ( + 3007, + 1, + RoveCommDataType.INT16_T + ), + // [R, G, B] (0, 255) + ["LEDRGB"] = new RoveCommPacketDesc + ( + 3008, + 3, + RoveCommDataType.UINT8_T + ), + // [Pattern] (Enum) + ["LEDPatterns"] = new RoveCommPacketDesc + ( + 3009, + 1, + RoveCommDataType.UINT8_T + ), + // [Teleop, Autonomy, Reached Goal] (enum) + ["StateDisplay"] = new RoveCommPacketDesc + ( + 3010, + 1, + RoveCommDataType.UINT8_T + ), + // Set Brightness (0-255) + ["Brightness"] = new RoveCommPacketDesc + ( + 3011, + 1, + RoveCommDataType.UINT8_T + ), + // 0: Teleop, 1: Autonomy + ["SetWatchdogMode"] = new RoveCommPacketDesc + ( + 3012, + 1, + RoveCommDataType.UINT8_T + ), + // Set the message to display on the lighting panel; null terminator ends string early + ["LEDText"] = new RoveCommPacketDesc + ( + 3013, + 256, + RoveCommDataType.CHAR + ) + }, + telemetry: new Dictionary + { + // [LF, LM, LR, RF, RM, RR] (-1, 1)-> (-100%, 100%) + ["DriveSpeeds"] = new RoveCommPacketDesc + ( + 3100, + 6, + RoveCommDataType.FLOAT + ), + // [Roll, Pitch, Yaw] degrees + ["IMUData"] = new RoveCommPacketDesc + ( + 3101, + 3, + RoveCommDataType.FLOAT + ), + // [xAxis, yAxis, zAxis] Accel in m/s^2 + ["AccelerometerData"] = new RoveCommPacketDesc + ( + 3102, + 3, + RoveCommDataType.FLOAT + ) + }, + errors: new Dictionary + { + + } + ), + ["PMS"] = new RoveCommBoardDesc + ( + ip: "192.168.2.102", + commands: new Dictionary + { + // Power off all systems except network (PMS will stay on) + ["EStop"] = new RoveCommPacketDesc + ( + 4000, + 1, + RoveCommDataType.UINT8_T + ), + // Power off all systems including network, cannot recover without physical reboot (PMS will stay on) + ["Suicide"] = new RoveCommPacketDesc + ( + 4001, + 1, + RoveCommDataType.UINT8_T + ), + // Cycle all systems including network off and back on (PMS will stay on) + ["Reboot"] = new RoveCommPacketDesc + ( + 4002, + 1, + RoveCommDataType.UINT8_T + ), + // [Motor, Core, Aux] (bitmasked) [1-Enable, 0-No change] + ["EnableBus"] = new RoveCommPacketDesc + ( + 4003, + 1, + RoveCommDataType.UINT8_T + ), + // [Motor, Core, Aux] (bitmasked) [1-Disable, 0-No change] + ["DisableBus"] = new RoveCommPacketDesc + ( + 4004, + 1, + RoveCommDataType.UINT8_T + ), + // [Motor, Core, Aux] (bitmasked) [1-Enable, 0-Disable] + ["SetBus"] = new RoveCommPacketDesc + ( + 4005, + 1, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // Total current draw from battery + ["PackCurrent"] = new RoveCommPacketDesc + ( + 4100, + 1, + RoveCommDataType.FLOAT + ), + // Pack voltage + ["PackVoltage"] = new RoveCommPacketDesc + ( + 4101, + 1, + RoveCommDataType.FLOAT + ), + // C1, C2, C3, C4, C5, C6 + ["CellVoltage"] = new RoveCommPacketDesc + ( + 4102, + 6, + RoveCommDataType.FLOAT + ), + // Current draw by aux systems (before 12V buck) + ["AuxCurrent"] = new RoveCommPacketDesc + ( + 4103, + 1, + RoveCommDataType.FLOAT + ), + // Current draw from other devices (CS1, CS2, CS3) + ["MiscCurrent"] = new RoveCommPacketDesc + ( + 4104, + 3, + RoveCommDataType.FLOAT + ), + // [Motor, Core, Aux, Network] (bitmasked) [1-Enabled, 0-Disabled] + ["BusStatus"] = new RoveCommPacketDesc + ( + 4105, + 1, + RoveCommDataType.UINT8_T + ) + }, + errors: new Dictionary + { + // Higher current draw than the battery can support. Rover will Reboot automatically + ["PackOvercurrent"] = new RoveCommPacketDesc + ( + 4200, + 1, + RoveCommDataType.UINT8_T + ), + // (bitmasked) [1-Undervolt, 0-OK]. Rover will EStop automatically + ["CellUndervoltage"] = new RoveCommPacketDesc + ( + 4201, + 1, + RoveCommDataType.UINT8_T + ), + // (bitmasked) [1-Critical, 0-OK]. Rover will Suicide automatically + ["CellCritical"] = new RoveCommPacketDesc + ( + 4202, + 1, + RoveCommDataType.UINT8_T + ), + // Aux system current draw too high. Rover will disable Aux bus automatically + ["AuxOvercurrent"] = new RoveCommPacketDesc + ( + 4203, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["Nav"] = new RoveCommBoardDesc + ( + ip: "192.168.2.104", + commands: new Dictionary + { + + }, + telemetry: new Dictionary + { + // [Lat, Long, Alt] [(-90, 90), (-180, 180)(deg), (0, 1000)] + ["GPSLatLonAlt"] = new RoveCommPacketDesc + ( + 6100, + 3, + RoveCommDataType.DOUBLE + ), + // [Pitch, Yaw, Roll] [(-90, 90), (0, 360), (-90, 90)] (deg) + ["IMUData"] = new RoveCommPacketDesc + ( + 6101, + 3, + RoveCommDataType.FLOAT + ), + // [Heading] [ 0, 360 ] + ["CompassData"] = new RoveCommPacketDesc + ( + 6102, + 1, + RoveCommDataType.FLOAT + ), + // [Number of satellites] + ["SatelliteCountData"] = new RoveCommPacketDesc + ( + 6103, + 1, + RoveCommDataType.UINT8_T + ), + // [xAxis, yAxis, zAxis] Accel in m/s^2 + ["AccelerometerData"] = new RoveCommPacketDesc + ( + 6104, + 3, + RoveCommDataType.FLOAT + ), + // [horizontal_accur, vertical_accur, heading_accur, fix_type, is_differentia] [meters, meters, degrees, ublox_navpvt fix type (http://docs.ros.org/en/noetic/api/ublox_msgs/html/msg/NavPVT.html), boolean] + ["AccuracyData"] = new RoveCommPacketDesc + ( + 6105, + 5, + RoveCommDataType.FLOAT + ) + }, + errors: new Dictionary + { + // + ["GPSLockError"] = new RoveCommPacketDesc + ( + 6200, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["BaseStationNav"] = new RoveCommBoardDesc + ( + ip: "192.168.100.112" + ), + ["SignalStack"] = new RoveCommBoardDesc + ( + ip: "192.168.100.101", + commands: new Dictionary + { + // Motor decipercent [-1000, 1000] + ["OpenLoop"] = new RoveCommPacketDesc + ( + 7000, + 1, + RoveCommDataType.INT16_T + ), + // [Heading] [0, 360) + ["SetAngleTarget"] = new RoveCommPacketDesc + ( + 7001, + 1, + RoveCommDataType.FLOAT + ), + // [Rover Lat, Rover Long, Basestation Lat, Basestation Long] [Lat:(-90, 90), Long:(-180, 180)] (deg) + ["SetGPSTarget"] = new RoveCommPacketDesc + ( + 7002, + 4, + RoveCommDataType.DOUBLE + ), + // [0-override off, 1-override on] + ["WatchdogOverride"] = new RoveCommPacketDesc + ( + 7003, + 1, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // [Heading] [0, 360) + ["CompassAngle"] = new RoveCommPacketDesc + ( + 7100, + 1, + RoveCommDataType.FLOAT + ) + }, + errors: new Dictionary + { + // (1-Watchdog timeout, 0-OK) + ["WatchdogStatus"] = new RoveCommPacketDesc + ( + 7200, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["Arm"] = new RoveCommBoardDesc + ( + ip: "192.168.2.107", + commands: new Dictionary + { + // [X, Y1, Y2, Z, P, R] Motor decipercent [-1000, 1000] + ["OpenLoop"] = new RoveCommPacketDesc + ( + 8000, + 6, + RoveCommDataType.INT16_T + ), + // [X, Y1, Y2, Z, P, R] (in, in, in, in, deg, deg) + ["SetPosition"] = new RoveCommPacketDesc + ( + 8001, + 6, + RoveCommDataType.FLOAT + ), + // [X, Y, Z, P, R] (in, in, in, deg, deg, deg) + ["IncrementPosition"] = new RoveCommPacketDesc + ( + 8002, + 5, + RoveCommDataType.FLOAT + ), + // [X, Y, Z, P, R] (in, in, in, deg, deg) + ["SetIK"] = new RoveCommPacketDesc + ( + 8003, + 5, + RoveCommDataType.FLOAT + ), + // [X, Y, Z, P, R] (in, in, in, deg, deg) + ["IncrementIK_RoverRelative"] = new RoveCommPacketDesc + ( + 8004, + 5, + RoveCommDataType.FLOAT + ), + // [X, Y, Z, P, R] (in, in, in, deg, deg) + ["IncrementIK_WristRelative"] = new RoveCommPacketDesc + ( + 8005, + 5, + RoveCommDataType.FLOAT + ), + // [0-disable, 1-enable] + ["Laser"] = new RoveCommPacketDesc + ( + 8006, + 1, + RoveCommDataType.UINT8_T + ), + // [0-retract, 1-extend] + ["Solenoid"] = new RoveCommPacketDesc + ( + 8007, + 1, + RoveCommDataType.UINT8_T + ), + // Motor decipercent [-1000, 1000] + ["Gripper"] = new RoveCommPacketDesc + ( + 8008, + 1, + RoveCommDataType.INT16_T + ), + // [0-override off, 1-override on] + ["WatchdogOverride"] = new RoveCommPacketDesc + ( + 8009, + 1, + RoveCommDataType.UINT8_T + ), + // [X+, X-, Y1+, Y1-, Y2+, Y2-, Z+, Z-, P+, P-] (0-override off, 1-override on) (bitmasked) + ["LimitSwitchOverride"] = new RoveCommPacketDesc + ( + 8010, + 1, + RoveCommDataType.UINT16_T + ), + // [X, Y1, Y2, Z, P, R1, R2] (1-calibrate, 0-no action) (bitmasked) + ["CalibrateEncoder"] = new RoveCommPacketDesc + ( + 8011, + 1, + RoveCommDataType.UINT8_T + ), + // Toggle gripper and roll motors controlled by other packets; 0-Gripper1, 1-Gripper2 + ["SelectGripper"] = new RoveCommPacketDesc + ( + 8012, + 1, + RoveCommDataType.UINT8_T + ), + // [X+, X-, Y1+, Y1-, Y2+, Y2-, Z+, Z-, P+, P-] (0-override off, 1-override on) (bitmasked) + ["SoftLimitOverride"] = new RoveCommPacketDesc + ( + 8013, + 1, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // [X, Y1, Y2, Z, Pitch, Roll1, Roll2] (in, in, in, in, deg, deg, deg) + ["Positions"] = new RoveCommPacketDesc + ( + 8100, + 7, + RoveCommDataType.FLOAT + ), + // [X, Y, Z, P, R] (in, in, in, deg, deg) + ["Coordinates"] = new RoveCommPacketDesc + ( + 8101, + 5, + RoveCommDataType.FLOAT + ), + // [X+, X-, Y1+, Y1-, Y2+, Y2-, Z+, Z-, Pitch+, Pitch-] (0-off, 1-on) (bitmasked) + ["LimitSwitchTriggered"] = new RoveCommPacketDesc + ( + 8102, + 1, + RoveCommDataType.UINT16_T + ) + }, + errors: new Dictionary + { + // (1-Watchdog timeout, 0-OK) + ["WatchdogStatus"] = new RoveCommPacketDesc + ( + 8200, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["ScienceActuation"] = new RoveCommBoardDesc + ( + ip: "192.168.2.108", + commands: new Dictionary + { + // Motor decipercent [-1000, 1000] + ["ScoopAxis_OpenLoop"] = new RoveCommPacketDesc + ( + 9000, + 1, + RoveCommDataType.INT16_T + ), + // Motor decipercent [-1000, 1000] + ["SensorAxis_OpenLoop"] = new RoveCommPacketDesc + ( + 9001, + 1, + RoveCommDataType.INT16_T + ), + // Absolute position (in) + ["ScoopAxis_SetPosition"] = new RoveCommPacketDesc + ( + 9002, + 1, + RoveCommDataType.FLOAT + ), + // Absolute position (in) + ["SensorAxis_SetPosition"] = new RoveCommPacketDesc + ( + 9003, + 1, + RoveCommDataType.FLOAT + ), + // (in) + ["ScoopAxis_IncrementPosition"] = new RoveCommPacketDesc + ( + 9004, + 1, + RoveCommDataType.FLOAT + ), + // (in) + ["SensorAxis_IncrementPosition"] = new RoveCommPacketDesc + ( + 9005, + 1, + RoveCommDataType.FLOAT + ), + // [ScoopAxis+, ScoopAxis-, SensorAxis+, SensorAxis-] (0-override off, 1-override on) (bitmasked) + ["LimitSwitchOverride"] = new RoveCommPacketDesc + ( + 9006, + 1, + RoveCommDataType.UINT8_T + ), + // Motor decipercent [-1000, 1000] + ["Auger"] = new RoveCommPacketDesc + ( + 9007, + 1, + RoveCommDataType.INT16_T + ), + // [0-180] (degrees) + ["Microscope"] = new RoveCommPacketDesc + ( + 9008, + 1, + RoveCommDataType.UINT8_T + ), + // [0-override off, 1-override on] + ["WatchdogOverride"] = new RoveCommPacketDesc + ( + 9010, + 1, + RoveCommDataType.UINT8_T + ), + // [ScoopAxis, SensorAxis, Proboscis] (1-calibrate, 0-no action) (bitmasked) + ["CalibrateEncoder"] = new RoveCommPacketDesc + ( + 9011, + 1, + RoveCommDataType.UINT8_T + ), + // Request the humidity of the instrument + ["RequestHumidity"] = new RoveCommPacketDesc + ( + 9012, + 1, + RoveCommDataType.UINT8_T + ), + // [Pan, Tilt](degrees -180-180) + ["AugerGimbalIncrement"] = new RoveCommPacketDesc + ( + 9013, + 2, + RoveCommDataType.INT16_T + ) + }, + telemetry: new Dictionary + { + // [ScoopAxis, SensorAxis] (in) + ["Positions"] = new RoveCommPacketDesc + ( + 9100, + 2, + RoveCommDataType.FLOAT + ), + // [ScoopAxis+, ScoopAxis-, SensorAxis+, SensorAxis-] (0-off, 1-on) (bitmasked) + ["LimitSwitchTriggered"] = new RoveCommPacketDesc + ( + 9101, + 1, + RoveCommDataType.UINT8_T + ), + // [Humidity] (relative humidity %) + ["Humidity"] = new RoveCommPacketDesc + ( + 9102, + 1, + RoveCommDataType.FLOAT + ), + // (in/s) + ["AugerSpeed"] = new RoveCommPacketDesc + ( + 9103, + 1, + RoveCommDataType.FLOAT + ) + }, + errors: new Dictionary + { + // (1-Watchdog timeout, 0-OK) + ["WatchdogStatus"] = new RoveCommPacketDesc + ( + 9200, + 1, + RoveCommDataType.UINT8_T + ), + // (1-Stalled, 0-OK) + ["AugerStalled"] = new RoveCommPacketDesc + ( + 9201, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["Autonomy"] = new RoveCommBoardDesc + ( + ip: "192.168.3.100", + commands: new Dictionary + { + // + ["StartAutonomy"] = new RoveCommPacketDesc + ( + 11000, + 1, + RoveCommDataType.UINT8_T + ), + // + ["DisableAutonomy"] = new RoveCommPacketDesc + ( + 11001, + 1, + RoveCommDataType.UINT8_T + ), + // [Lat, Lon] + ["AddPositionLeg"] = new RoveCommPacketDesc + ( + 11002, + 2, + RoveCommDataType.DOUBLE + ), + // [Lat, Lon] + ["AddMarkerLeg"] = new RoveCommPacketDesc + ( + 11003, + 2, + RoveCommDataType.DOUBLE + ), + // [Lat, Lon] + ["AddObjectLeg"] = new RoveCommPacketDesc + ( + 11004, + 2, + RoveCommDataType.DOUBLE + ), + // + ["ClearWaypoints"] = new RoveCommPacketDesc + ( + 11005, + 1, + RoveCommDataType.UINT8_T + ), + // A multiplier from 0.0 to 1.0 that will scale the max power effort of Autonomy + ["SetMaxSpeed"] = new RoveCommPacketDesc + ( + 11006, + 1, + RoveCommDataType.FLOAT + ), + // [Enum (AUTONOMYLOG), Enum (AUTONOMYLOG), Enum (AUTONOMYLOG)] {Console, File, RoveComm} + ["SetLoggingLevels"] = new RoveCommPacketDesc + ( + 11007, + 3, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // Enum (AUTONOMYSTATE) + ["CurrentState"] = new RoveCommPacketDesc + ( + 11100, + 1, + RoveCommDataType.UINT8_T + ), + // + ["ReachedGoal"] = new RoveCommPacketDesc + ( + 11101, + 1, + RoveCommDataType.UINT8_T + ), + // String version of most current error log + ["CurrentLog"] = new RoveCommPacketDesc + ( + 11102, + 255, + RoveCommDataType.CHAR + ) + }, + errors: new Dictionary + { + + } + ), + ["Camera1"] = new RoveCommBoardDesc + ( + ip: "192.168.4.100", + commands: new Dictionary + { + // Change which camera a feed is looking at. [0] is the feed, [1] is the camera to view. + ["ChangeCameras"] = new RoveCommPacketDesc + ( + 12000, + 2, + RoveCommDataType.UINT8_T + ), + // Take a picture with the current camera. [0] is the camera to take a picture with. [1] tells the camera whether to restart the stream afterwards. + ["TakePicture"] = new RoveCommPacketDesc + ( + 12001, + 2, + RoveCommDataType.UINT8_T + ), + // Stop the current camera stream. [0] is the camera to stop streaming. [1] is whether to restart the stream. + ["ToggleStream1"] = new RoveCommPacketDesc + ( + 12002, + 2, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // Bitmask values for which cameras are able to stream. LSB is Camera 0, MSB is Camera 7. + ["AvailableCameras"] = new RoveCommPacketDesc + ( + 12100, + 1, + RoveCommDataType.UINT8_T + ), + // Which cameras the system is currently streaming on each port + ["StreamingCameras"] = new RoveCommPacketDesc + ( + 12101, + 4, + RoveCommDataType.UINT8_T + ), + // Picture has been taken. + ["PictureTaken1"] = new RoveCommPacketDesc + ( + 12102, + 1, + RoveCommDataType.UINT8_T + ) + }, + errors: new Dictionary + { + // Camera has errored and stopped streaming. [0] is ID of camera as an integer (not bitmask). + ["CameraUnavailable"] = new RoveCommPacketDesc + ( + 12200, + 1, + RoveCommDataType.UINT8_T + ) + } + ), + ["Camera2"] = new RoveCommBoardDesc + ( + ip: "192.168.4.101", + commands: new Dictionary + { + // Take a picture with the current camera. [0] is the camera to take a picture with. [1] tells the camera whether to restart the stream afterwards. + ["TakePicture"] = new RoveCommPacketDesc + ( + 13001, + 1, + RoveCommDataType.UINT8_T + ), + // Stop the current camera stream. [0] is the camera to stop streaming. [1] is whether to restart the stream. + ["ToggleStream2"] = new RoveCommPacketDesc + ( + 13002, + 2, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // Picture has been taken. + ["PictureTaken2"] = new RoveCommPacketDesc + ( + 13100, + 1, + RoveCommDataType.UINT8_T + ) + }, + errors: new Dictionary + { + + } + ), + ["IRSpectrometer"] = new RoveCommBoardDesc + ( + ip: "192.168.3.104", + commands: new Dictionary + { + + }, + telemetry: new Dictionary + { + + }, + errors: new Dictionary + { + + } + ), + ["Instruments"] = new RoveCommBoardDesc + ( + ip: "192.168.3.105", + commands: new Dictionary + { + // [Green, White] [1-Enabled, 0-Disabled] (bitmasked) + ["EnableLEDs"] = new RoveCommPacketDesc + ( + 16000, + 1, + RoveCommDataType.UINT8_T + ), + // Start a Raman reading, with the provided integration time (milliseconds) + ["RequestRamanReading"] = new RoveCommPacketDesc + ( + 16001, + 1, + RoveCommDataType.UINT32_T + ), + // Start a Reflectance reading, with the provided integration time (milliseconds) + ["RequestReflectanceReading"] = new RoveCommPacketDesc + ( + 16002, + 1, + RoveCommDataType.UINT32_T + ), + // Request the temperature of the instrument + ["RequestTemperature"] = new RoveCommPacketDesc + ( + 16003, + 1, + RoveCommDataType.UINT8_T + ) + }, + telemetry: new Dictionary + { + // Raman CCD elements 1-500 + ["RamanReading_Part1"] = new RoveCommPacketDesc + ( + 16100, + 500, + RoveCommDataType.UINT16_T + ), + // Raman CCD elements 501-1000 + ["RamanReading_Part2"] = new RoveCommPacketDesc + ( + 16101, + 500, + RoveCommDataType.UINT16_T + ), + // Raman CCD elements 1001-1500 + ["RamanReading_Part3"] = new RoveCommPacketDesc + ( + 16102, + 500, + RoveCommDataType.UINT16_T + ), + // Raman CCD elements 1501-2000 + ["RamanReading_Part4"] = new RoveCommPacketDesc + ( + 16103, + 500, + RoveCommDataType.UINT16_T + ), + // Raman CCD elements 2001-2048 + ["RamanReading_Part5"] = new RoveCommPacketDesc + ( + 16104, + 48, + RoveCommDataType.UINT16_T + ), + // Reflectance CCD elements 1-288 + ["ReflectanceReading"] = new RoveCommPacketDesc + ( + 16105, + 288, + RoveCommDataType.UINT8_T + ), + // [Temperature] (degrees C) + ["Temperature"] = new RoveCommPacketDesc + ( + 16106, + 1, + RoveCommDataType.INT8_T + ) + }, + errors: new Dictionary + { + + } + ) + }; +} diff --git a/Basestation_Software.Models/RoveComm/RoveCommPacket.cs b/Basestation_Software.Models/RoveComm/RoveCommPacket.cs new file mode 100644 index 0000000..9d5e61d --- /dev/null +++ b/Basestation_Software.Models/RoveComm/RoveCommPacket.cs @@ -0,0 +1,45 @@ +namespace Basestation_Software.Models.RoveComm; + +/// +/// RoveComm Packet Format: +/// +/// 0 1 2 3 4 5 +/// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Version | DataID | DataCount | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// | Data Type | Data (Variable) ... | +/// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +/// +/// Note: the size of Data is DataCount * DataTypeSize(DataType) +/// +/// One of: sbyte, byte, short, ushort, int, uint, float, double, or char. +public class RoveCommPacket +{ + public int DataID { get; set; } + public int DataCount { get { return Data.Count; } } + public RoveCommDataType DataType { get; init; } + public List Data { get; set; } + + public RoveCommPacket(int dataId, List data) + { + DataID = dataId; + Data = data; + DataType = RoveCommUtils.DataTypeFromType(typeof(T)); + } + + public RoveCommPacket(int dataId, int dataCount) : + this(dataId, new List(dataCount)) + { } + + public RoveCommHeader GetHeader() + { + return new RoveCommHeader + { + Version = (byte)RoveCommConsts.RoveCommVersion, + DataID = (short)DataID, + DataCount = (short)DataCount, + DataType = (byte)DataType, + }; + } +} diff --git a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs new file mode 100644 index 0000000..2b562a5 --- /dev/null +++ b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs @@ -0,0 +1,400 @@ +using System.Buffers.Binary; +using System.Runtime.InteropServices; + +namespace Basestation_Software.Models.RoveComm; + +/// +/// A simple helper struct representing the metadata about a packet as read from the network. +/// +public struct RoveCommHeader +{ + public byte Version; + public short DataID; + public short DataCount; + public byte DataType; +} + +public class RoveCommException : Exception +{ + public RoveCommException(string message) : base(message) { } + public RoveCommException(string message, Exception inner) : base(message, inner) { } +} + +/// +/// Helper functions used by RoveComm internally. +/// +public static class RoveCommUtils +{ + /// + /// Convert an integer to a RoveCommDataType safely. + /// + /// The integer to convert. + /// The associated RoveCommDataType. + /// + /// Thrown if the integer does not match a RoveCommDataType. + /// + public static RoveCommDataType ParseDataType(int type) + { + if (Enum.IsDefined(typeof(RoveCommDataType), type)) + { + return (RoveCommDataType)type; + } + else + { + throw new RoveCommException("Failed to convert int to RoveCommDataType."); + } + } + + /// + /// Get the size of a RoveCommDataType. + /// + /// The RoveCommDataType to get the size of. + /// The size in bytes of the RoveCommDataType. + /// + /// Thrown if the RoveCommDataType is unknown. + /// + public static int DataTypeSize(RoveCommDataType type) + { + return type switch + { + RoveCommDataType.INT8_T => 1, + RoveCommDataType.UINT8_T => 1, + RoveCommDataType.INT16_T => 2, + RoveCommDataType.UINT16_T => 2, + RoveCommDataType.INT32_T => 4, + RoveCommDataType.UINT32_T => 4, + RoveCommDataType.FLOAT => 4, + RoveCommDataType.DOUBLE => 8, + RoveCommDataType.CHAR => 1, + _ => throw new RoveCommException("Failed to find size of unknown RoveCommDataType."), // unreachable + }; + } + + /// + /// Convert a System.Type to a RoveCommDataType. + /// + /// The System.Type to convert. + /// The associated RoveCommDataType. + /// + /// Thrown if the System.Type did not match a RoveCommDataType. + /// + public static RoveCommDataType DataTypeFromType(Type type) + { + TypeCode code = Type.GetTypeCode(type); + return code switch + { + TypeCode.SByte => RoveCommDataType.INT8_T, + TypeCode.Byte => RoveCommDataType.UINT8_T, + TypeCode.Int16 => RoveCommDataType.INT16_T, + TypeCode.UInt16 => RoveCommDataType.UINT16_T, + TypeCode.Int32 => RoveCommDataType.INT32_T, + TypeCode.UInt32 => RoveCommDataType.UINT32_T, + TypeCode.Single => RoveCommDataType.FLOAT, + TypeCode.Double => RoveCommDataType.DOUBLE, + TypeCode.Char => RoveCommDataType.CHAR, + _ => throw new RoveCommException("Failed to create RoveCommDataType from unknown System.Type."), + }; + } + + + /// + /// Read a RoveCommPacket header from the given byte buffer. + /// + /// The byte buffer to read; assumed to be in Big Endian. + /// The parsed RoveCommHeader. + /// + /// Thrown if a RoveCommHeader could not be read from the given buffer. + /// + public static RoveCommHeader ParseHeader(ReadOnlySpan data) + { + if (data.Length < RoveCommConsts.HeaderSize) + { + throw new RoveCommException("Failed to parse RoveCommHeader: not enough bytes."); + } + else + { + if (data[0] != RoveCommConsts.RoveCommVersion) + { + throw new RoveCommException("Failed to parse RoveCommHeader: invalid version."); + } + + return new RoveCommHeader + { + Version = data[0], + DataID = BinaryPrimitives.ReadInt16BigEndian(data.Slice(1, 2)), + DataCount = BinaryPrimitives.ReadInt16BigEndian(data.Slice(3, 2)), + DataType = data[5] + }; + } + } + /// + /// Pack a RoveCommHeader into the given byte buffer. + /// + /// The byte buffer in which to pack the header. + /// The RoveCommHeader to pack. + /// + /// Thrown if the given buffer is too small to hold the header. + /// + public static void PackHeader(Span dest, RoveCommHeader header) + { + if (dest.Length < RoveCommConsts.HeaderSize) + { + throw new RoveCommException("Failed to pack RoveCommHeader: the given buffer is too small for the header."); + } + + dest[0] = (byte)RoveCommConsts.RoveCommVersion; + BinaryPrimitives.WriteInt16BigEndian(dest.Slice(1, 2), header.DataID); + BinaryPrimitives.WriteInt16BigEndian(dest.Slice(3, 2), header.DataCount); + dest[5] = header.DataType; + } + + + /// + /// Read a RoveCommPacket from the given byte buffer. + /// + /// The byte buffer to read; assumed to be in Big Endian. + /// The parsed RoveCommPacket. + /// + /// Thrown if a RoveCommPacket could not be read from the given buffer. + /// + public static RoveCommPacket ParsePacket(ReadOnlySpan data) + { + // Parse header to get metadata -- will error if there isn't enough data to parse the header. + RoveCommHeader header = ParseHeader(data); + if (data[0] != RoveCommConsts.RoveCommVersion) + { + throw new RoveCommException("Failed to parse RoveCommHeader: invalid version."); + } + // Make sure packet data size is not over the maximum size. + int dataSize = DataTypeSize(ParseDataType(header.DataType)) * header.DataCount; + if (dataSize > RoveCommConsts.MaxDataSize) + { + throw new RoveCommException("Failed to parse RoveCommPacket: max packet size exceeded."); + } + // Packet create new packet to write to. + RoveCommPacket packet = new RoveCommPacket(header.DataID, header.DataCount); + + // Create a slice to the data portion of the packet. + var dataBuf = data.Slice(RoveCommConsts.HeaderSize); + // We might have received a packet that isn't as long as it claims to be. + if (dataBuf.Length != dataSize) + { + throw new RoveCommException("Failed to parse RoveCommPacket: not enough data from network to fill packet."); + } + // Read data from the packet from the data buffer. Remember: network byte order is Big Endian! + switch (packet.Data) + { + // SEXY PATTERN MATCHING + case List packetData: + { + var casted = MemoryMarshal.Cast(dataBuf); + for (int i = 0; i < header.DataCount; i++) packetData.Add(casted[i]); + break; + } + case List packetData: + { + // No cast necessary -- already bytes. + for (int i = 0; i < header.DataCount; i++) packetData.Add(dataBuf[i]); + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadInt16BigEndian(dataBuf.Slice(i * 2, 2))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadUInt16BigEndian(dataBuf.Slice(i * 2, 2))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadInt32BigEndian(dataBuf.Slice(i * 4, 4))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadUInt32BigEndian(dataBuf.Slice(i * 4, 4))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadUInt32BigEndian(dataBuf.Slice(i * 4, 4))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + packetData.Add(BinaryPrimitives.ReadDoubleBigEndian(dataBuf.Slice(i * 8, 8))); + } + + break; + } + case List packetData: + { + for (int i = 0; i < header.DataCount; i++) + { + // Careful: C# stores chars in UTF-16, so they are 2 bytes wide. + // This can cause encoding errors if converting byte[] directly to string. + packetData.Add((char)dataBuf[i]); + } + + break; + } + default: + { + throw new RoveCommException("Failed to parse RoveCommPacket: invalid data type."); + } + } + + return packet; + } + /// + /// Pack a RoveCommPacket into a byte array. + /// + /// The packet to pack. + /// A byte array packed in Big Endian. + /// + /// Thrown if the given packet is too large. + /// + public static byte[] PackPacket(RoveCommPacket packet) + { + int dataSize = packet.DataCount * DataTypeSize(packet.DataType); + if (dataSize > RoveCommConsts.MaxDataSize) + { + throw new RoveCommException("Failed to pack RoveCommPacket: payload exceeds max data size."); + } + byte[] dataBuf = new byte[RoveCommConsts.HeaderSize + dataSize]; + PackPacket(dataBuf, packet); + return dataBuf; + } + public static void PackPacket(Span dest, RoveCommPacket packet) + { + // Make sure the packet and destination buffer are within size constraints. + int packetSize = RoveCommConsts.HeaderSize + packet.DataCount * DataTypeSize(packet.DataType); + if (dest.Length < packetSize) + { + throw new RoveCommException("Failed to pack RoveCommPacket: the given buffer is too small to hold the packet."); + } + if (packetSize > RoveCommConsts.HeaderSize + RoveCommConsts.MaxDataSize) + { + throw new RoveCommException("Failed to pack RoveCommPacket: payload exceeds max data size."); + } + // Pack header in network byte order. + PackHeader(dest, packet.GetHeader()); + // Create a slice to the data portion of the data buffer. + var dataBuf = dest.Slice(RoveCommConsts.HeaderSize); + // Pack data in network byte order. + switch (packet.Data) + { + case List packetData: + { + var casted = MemoryMarshal.Cast(dataBuf); + for (int i = 0; i < packet.DataCount; i++) + { + casted[i] = packetData[i]; + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + dataBuf[0] = packetData[0]; + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteUInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteUInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteSingleBigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + BinaryPrimitives.WriteDoubleBigEndian(dataBuf.Slice(i * 8, 8), packetData[i]); + } + + break; + } + case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) + { + dataBuf[i] = (byte)packetData[i]; + } + + break; + } + default: + { + throw new RoveCommException("Failed to pack RoveCommPacket: invalid data type."); + } + } + } +} + diff --git a/Basestation_Software.Web/Core/Components/RoveCommExample.razor b/Basestation_Software.Web/Core/Components/RoveCommExample.razor new file mode 100644 index 0000000..28b486c --- /dev/null +++ b/Basestation_Software.Web/Core/Components/RoveCommExample.razor @@ -0,0 +1,91 @@ +@implements IAsyncDisposable +@inject RoveCommService _RoveCommService +@using Basestation_Software.Models.RoveComm + +
+
+
RoveComm Test
+
+
+
+
+ + + +
+

@_message

+ @if (!_dataLoaded) + { +
+
+
+

Waiting for packets...

+
+
+ } + else + { +
    + @foreach(var message in _messages) + { +
  • @message
  • + } +
+ } +
+
+
+ + +@code +{ + private bool _dataLoaded = false; + private string? _message; + private bool _isDisabled; + private readonly List _messages = []; + + protected override async Task OnInitializedAsync() + { + // Subscribe to all DataIDs. + _RoveCommService.On(0, AddMessage); + + await InvokeAsync(StateHasChanged); + } + + public async Task AddMessage(List payload) + { + _messages.Add($"Received Packet. Data: {string.Join(", ", payload)}"); + _dataLoaded = true; + await InvokeAsync(StateHasChanged); + } + + public async Task SendPacketTCP() + { + await _RoveCommService.SendAsync(6969, [1, 2, 3], "127.0.0.1", reliable: true); + } + public async Task SendPacketUDP() + { + await _RoveCommService.SendAsync(6969, [1, 2, 3], "127.0.0.1", reliable: false); + } + public async Task ListenForPacket() + { + _message = "Waiting..."; + _isDisabled = true; + var result = await _RoveCommService.Listen(0, 3000); + if (result is not null) + { + _message = "Received!"; + } + else + { + _message = "Timed out."; + } + _isDisabled = false; + } + + public async ValueTask DisposeAsync() + { + _RoveCommService.Clear(AddMessage); + await Task.CompletedTask; + } +} diff --git a/Basestation_Software.Web/Core/Pages/RON.razor b/Basestation_Software.Web/Core/Pages/RON.razor index df23929..224b3b5 100644 --- a/Basestation_Software.Web/Core/Pages/RON.razor +++ b/Basestation_Software.Web/Core/Pages/RON.razor @@ -5,6 +5,9 @@
+
+ +
@code diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs new file mode 100644 index 0000000..76dece2 --- /dev/null +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -0,0 +1,404 @@ +using Basestation_Software.Models.RoveComm; + +namespace Basestation_Software.Web.Core.Services.RoveComm; + +/// +/// A function to be called by RoveComm when a relevant RoveCommPacket is received. +/// +/// The Data field of the received RoveCommPacket. +public delegate Task RoveCommCallback(List payload); + +public class RoveCommService : IHostedService +{ + + public RoveCommUDP UDP; + public RoveCommTCP TCP; + + private readonly CancellationTokenSource _cts = new(); + private readonly ILogger _logger; + + public RoveCommService(ILogger logger) + { + _logger = logger; + UDP = new RoveCommUDP(_logger); + TCP = new RoveCommTCP(_logger); + } + + public Task StartAsync(CancellationToken cancelToken) + { + Begin(cancelToken); + SubscribeAll(); + return Task.CompletedTask; + } + + public void Begin(CancellationToken cancelToken) + { + _logger.LogInformation("Starting RoveComm."); + UDP.Begin(cancelToken); + TCP.Begin(cancelToken); + } + + /// + /// Attach the given callback to be triggered when a RoveCommPacket with the given DataID is received. + /// + /// The DataID to listen for. + /// The function to call when the DataID is received. + /// Thrown if the type was invalid. + public void On(int dataId, RoveCommCallback handler) + { + TCP.On(dataId, handler); + UDP.On(dataId, handler); + _logger.LogInformation($"Subscribed to {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}"); + } + + /// + /// Attach the given callback to be triggered when a RoveCommPacket from the Manifest is received. + /// + /// The name of the board as shown in the Manifest. + /// The name of the Telemetry or Error message as shown in the Manifest. + /// The function to call when the DataID is received. + /// + /// Thrown if the packet descriptor was not found in the Manifest or did not match the given type. + /// + public void On(string boardName, string dataIdString, RoveCommCallback handler) + { + RoveCommPacketDesc? packetDesc; + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + var boardDesc = RoveCommManifest.Boards[boardName]; + if (boardDesc.Telemetry.ContainsKey(dataIdString)) + { + packetDesc = boardDesc.Telemetry[dataIdString]; + } + else if (boardDesc.Errors.ContainsKey(dataIdString)) + { + packetDesc = boardDesc.Errors[dataIdString]; + } + else + { + throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); + } + } + else + { + throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + if (packetDesc.DataType != handlerType) + { + throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString}."); + } + + On(packetDesc.DataID, handler); + } + + /// + /// Clear the given callback from all DataID's. + /// + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(RoveCommCallback handler) + { + UDP.Clear(handler); + TCP.Clear(handler); + } + + /// + /// Clear the given callback from the given DataID. + /// + /// The DataID to remove the callback from. + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(int dataId, RoveCommCallback handler) + { + UDP.Clear(dataId, handler); + TCP.Clear(dataId, handler); + } + + /// + /// Clear the given callback from the given Manifest entry. + /// + /// The name of the board as shown in the Manifest. + /// The name of the Telemetry or Error message as shown in the Manifest. + /// The callback to remove. + /// /// + /// Thrown if the packet descriptor was not found in the Manifest or did not match the given type. + /// + public void Clear(string boardName, string dataIdString, RoveCommCallback handler) + { + RoveCommPacketDesc? packetDesc; + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + var boardDesc = RoveCommManifest.Boards[boardName]; + if (boardDesc.Telemetry.ContainsKey(dataIdString)) + { + packetDesc = boardDesc.Telemetry[dataIdString]; + } + else if (boardDesc.Errors.ContainsKey(dataIdString)) + { + packetDesc = boardDesc.Errors[dataIdString]; + } + else + { + throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); + } + } + else + { + throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + if (packetDesc.DataType != handlerType) + { + throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString}."); + } + + UDP.Clear(packetDesc.DataID, handler); + TCP.Clear(packetDesc.DataID, handler); + } + + /// + /// Send data over RoveComm. + /// + /// The DataID of the packet. + /// The Data to send. + /// The IP to send to. + /// The port to send to. + /// Send over TCP if true, send over UDP if false. + /// True if the packet was sent successfully. + public bool Send(int dataId, List data, string ip, int port, bool reliable = false) + { + var packet = new RoveCommPacket(dataId, data); + return reliable ? TCP.Send(packet, ip, port) : UDP.Send(packet, ip, port); + } + public bool Send(int dataId, List data, string ip, bool reliable = false) + { + var packet = new RoveCommPacket(dataId, data); + return reliable ? TCP.Send(packet, ip) : UDP.Send(packet, ip); + } + + /// + /// Send data asynchronously over RoveComm. + /// + /// The DataID of the packet. + /// The Data to send. + /// The IP to send to. + /// The port to send to. + /// Send over TCP if true, send over UDP if false. + /// True if the packet was sent successfully. + public async Task SendAsync(int dataId, List data, string ip, int port, bool reliable = false, CancellationToken cancelToken = default) + { + var packet = new RoveCommPacket(dataId, data); + return reliable ? await TCP.SendAsync(packet, ip, port, cancelToken) : await UDP.SendAsync(packet, ip, port, cancelToken); + } + public async Task SendAsync(int dataId, List data, string ip, bool reliable = false, CancellationToken cancelToken = default) + { + var packet = new RoveCommPacket(dataId, data); + return reliable ? await TCP.SendAsync(packet, ip, cancelToken) : await UDP.SendAsync(packet, ip, cancelToken); + } + + /// + /// Send a command from the Manifest over RoveComm. + /// + /// The name of the board as shown in the Manifest. + /// The name of the command as shown in the Manifest. + /// The data to send with the command. + /// Send over TCP if true, send over UDP if false. + /// True if the packet was sent successfully. + /// + /// Thrown if the packet descriptor was not found in the Manifest or did not match the Manifest's schema. + /// + public bool Send(string boardName, string commandName, List data, bool reliable = false) + { + RoveCommBoardDesc? boardDesc; + RoveCommPacketDesc? packetDesc; + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + boardDesc = RoveCommManifest.Boards[boardName]; + if (boardDesc.Commands.ContainsKey(commandName)) + { + packetDesc = boardDesc.Commands[commandName]; + } + else if (boardDesc.Telemetry.ContainsKey(commandName)) + { + packetDesc = boardDesc.Telemetry[commandName]; + } + else if (boardDesc.Errors.ContainsKey(commandName)) + { + packetDesc = boardDesc.Errors[commandName]; + } + else + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } + } + else + { + throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + if (packetDesc.DataType != handlerType) + { + throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName}."); + } + + if (data.Count != packetDesc.DataCount) + { + throw new Exception($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); + } + + return Send(packetDesc.DataID, data, boardDesc.IP, reliable); + } + + /// + /// Send a command from the Manifest over RoveComm asynchronously. + /// + /// The name of the board as shown in the Manifest. + /// The name of the command as shown in the Manifest. + /// The data to send with the command. + /// Send over TCP if true, send over UDP if false. + /// True if the packet was sent successfully. + /// + /// Thrown if the packet descriptor was not found in the Manifest or did not match the Manifest's schema. + /// + public async Task SendAsync(string boardName, string commandName, List data, bool reliable = false, CancellationToken cancelToken = default) + { + RoveCommBoardDesc? boardDesc; + RoveCommPacketDesc? packetDesc; + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + boardDesc = RoveCommManifest.Boards[boardName]; + if (boardDesc.Commands.ContainsKey(commandName)) + { + packetDesc = boardDesc.Commands[commandName]; + } + else if (boardDesc.Telemetry.ContainsKey(commandName)) + { + packetDesc = boardDesc.Telemetry[commandName]; + } + else if (boardDesc.Errors.ContainsKey(commandName)) + { + packetDesc = boardDesc.Errors[commandName]; + } + else + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } + } + else + { + throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + if (packetDesc.DataType != handlerType) + { + throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName}."); + } + + if (data.Count != packetDesc.DataCount) + { + throw new Exception($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); + } + + return await SendAsync(packetDesc.DataID, data, boardDesc.IP, reliable, cancelToken); + } + + /// + /// Wait asynchronously until a packet with the desired DataID arrives. + /// + /// The DataID to listen for. + /// The number of milliseconds before returning null. + /// The payload if a packet is received within the timeout, null if none. + public async Task?> Listen(int dataId, int timeout = 30_000) + { + var promise = new TaskCompletionSource>(); + RoveCommCallback callback = async (payload) => { + promise.SetResult(payload); + await Task.CompletedTask; + }; + + On(dataId, callback); + + var cts = new CancellationTokenSource(); + var cancel = cts.Token; + cts.CancelAfter(timeout); + + using (cancel.Register(() => { + Clear(dataId, callback); + })) + { + try + { + return await promise.Task.WaitAsync(cancel); + } + catch (OperationCanceledException) + { + return null; + } + } + } + + public void Subscribe(string boardName) + { + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + Send(RoveCommManifest.SystemPackets.SUBSCRIBE, [1], RoveCommManifest.Boards[boardName].IP); + } + } + public void SubscribeAll() + { + foreach (var (_, board) in RoveCommManifest.Boards) + { + Send(RoveCommManifest.SystemPackets.SUBSCRIBE, [1], board.IP); + } + } + + public void Unubscribe(string boardName) + { + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + Send(RoveCommManifest.SystemPackets.UNSUBSCRIBE, [1], RoveCommManifest.Boards[boardName].IP); + } + } + public void UnsubscribeAll() + { + foreach (var (_, board) in RoveCommManifest.Boards) + { + Send(RoveCommManifest.SystemPackets.UNSUBSCRIBE, [1], board.IP); + } + } + + public void Ping(string boardName) + { + if (RoveCommManifest.Boards.ContainsKey(boardName)) + { + Send(RoveCommManifest.SystemPackets.PING, [1], RoveCommManifest.Boards[boardName].IP); + } + } + public void PingAll() + { + foreach (var (_, board) in RoveCommManifest.Boards) + { + Send(RoveCommManifest.SystemPackets.PING, [1], board.IP); + } + } + + public Task StopAsync(CancellationToken cancelToken) + { + UnsubscribeAll(); + Stop(); + return Task.CompletedTask; + } + + public void Stop() + { + _logger.LogInformation("Closing RoveComm."); + _cts.Cancel(); + UDP.Dispose(); + TCP.Dispose(); + } +} diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs new file mode 100644 index 0000000..6a9330a --- /dev/null +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -0,0 +1,460 @@ +using System.Net; +using System.Net.Sockets; +using Basestation_Software.Models.RoveComm; + +namespace Basestation_Software.Web.Core.Services.RoveComm; + +public class RoveCommTCP +{ + public bool Running { get; private set; } + public int Port { get; private set; } + private readonly List _connections = []; + private TcpListener? _TCPServer; + private readonly ILogger? _logger; + + private class RoveCommEmitter + { + public event RoveCommCallback? Notifier; + public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet.Data); + } + + private readonly Dictionary> _callbacksInt8 = []; + private readonly Dictionary> _callbacksUInt8 = []; + private readonly Dictionary> _callbacksInt16 = []; + private readonly Dictionary> _callbacksUInt16 = []; + private readonly Dictionary> _callbacksInt32 = []; + private readonly Dictionary> _callbacksUInt32 = []; + private readonly Dictionary> _callbacksFloat = []; + private readonly Dictionary> _callbacksDouble = []; + private readonly Dictionary> _callbacksChar = []; + + public RoveCommTCP(int port, ILogger? logger = null) + { + Port = port; + _logger = logger; + } + public RoveCommTCP(ILogger? logger = null) : this(RoveCommConsts.TCPPort, logger) { } + + /// + /// Begin accepting TCP connections and reading packets from the network. + /// + public void Begin(CancellationToken cancelToken) + { + if (Running) + { + _logger?.LogWarning("RoveComm TCP already started."); + return; + } + // Begin listening for TCP connection requests. Allow up to 10 pending requests at once. + _logger?.LogInformation($"Starting RoveComm TCP on port {Port}."); + _TCPServer = new TcpListener(IPAddress.Any, Port); + try + { + _TCPServer!.Start(10); + } + catch (Exception e) + { + _logger?.LogError($"Failed to start RoveComm TCP: {e.Message}"); + return; + } + Running = true; + Task.Run(async () => + { + try + { + while (!cancelToken.IsCancellationRequested) + { + // Accept any incoming connections. + if (_TCPServer!.Pending()) + { + TcpClient client = await _TCPServer!.AcceptTcpClientAsync(cancelToken); + _connections.Add(client); + _logger?.LogError($"Established connection with {client.Client.RemoteEndPoint}"); + } + // Read packets and trigger callbacks. + await ReceiveAndCallback(cancelToken); + // Don't hog the async queue. + await Task.Yield(); + } + } + catch (Exception e) + { + _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); + } + finally + { + // Close connections. + Stop(); + } + }, cancelToken); + } + + /// + /// Stop accepting TCP connections and reading packets from the network. + /// + public void Stop() + { + if (!Running) + { + _logger?.LogWarning("RoveComm TCP already stopped."); + return; + } + + Running = false; + // Close all connections. + foreach (var connection in _connections) + { + connection.Close(); + } + _connections.Clear(); + // Close listening socket. + try + { + _TCPServer!.Stop(); + } + catch (Exception e) + { + _logger?.LogError($"Something went wrong closing RoveComm TCP: {e.Message}"); + return; + } + _logger?.LogInformation("Closed RoveComm TCP."); + } + + // Find existing connection in conneciton list. + private TcpClient? _findExisting(IPEndPoint remote) + { + foreach (var connection in _connections) + { + var connectionEp = connection.Client.RemoteEndPoint as IPEndPoint; + if (connectionEp is null) + { + return null; + } + else if (connectionEp.Port == remote.Port && connectionEp.Address.Equals(remote.Address)) + { + // _logger?.LogInformation("Found existing conneciton."); + return connection; + } + } + return null; + } + + // Remove all connections that have disconnected. + private void _removeDisconnectedClients() + { + _connections.RemoveAll(connection => + { + if (!connection.Connected) + { + _logger?.LogInformation($"Disconnected from {connection.Client.RemoteEndPoint as IPEndPoint}."); + return true; + } + else + { + return false; + } + }); + } + + /// + /// Send a RoveCommPacket to the given IP and port, guaranteeing delivery. + /// + /// The RoveCommPacket to send. + /// The IP to send to. + /// The port to send to. + /// True if the packet was sent successfully. + /// + public bool Send(RoveCommPacket packet, string ip, int port) + { + if (!Running) + { + throw new RoveCommException("Failed to send TCP packet: RoveComm TCP not started."); + } + + _removeDisconnectedClients(); + + var dest = new IPEndPoint(IPAddress.Parse(ip), port); + // Check if there is already a connection with that endpoint. + TcpClient? client = _findExisting(dest); + + // If no existing connection was found, open a new one. + if (client is null) + { + try + { + _logger?.LogInformation($"Attempting to establish a connection with {dest}."); + client = new TcpClient(AddressFamily.InterNetwork); + client.Connect(dest); + _logger?.LogInformation($"Established connection with {client.Client.RemoteEndPoint as IPEndPoint}"); + } + catch (Exception e) + { + _logger?.LogError($"Failed to connect to remote host: {e.Message}"); + return false; + } + _connections.Add(client); + } + // Write the packet to the client's NetworkStream. + try + { + client.GetStream().Write(RoveCommUtils.PackPacket(packet)); + } + catch (Exception e) + { + _logger?.LogError($"Failed to send TCP packet: {e.Message}"); + return false; + } + _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + return true; + } + public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); + + /// + /// Send a RoveCommPacket to the given IP and port asynchronously. + /// + /// The RoveCommPacket to send. + /// The IP to send to. + /// The port to send to. + /// True if the packet was sent successfully. + /// + public async Task SendAsync(RoveCommPacket packet, string ip, int port, CancellationToken cancelToken = default) + { + if (!Running) + { + throw new RoveCommException("Failed to send TCP packet: RoveComm TCP not started."); + } + + _removeDisconnectedClients(); + + var dest = new IPEndPoint(IPAddress.Parse(ip), port); + // Check if there is already a connection with that endpoint. + TcpClient? client = _findExisting(dest); + // If no existing connection was found, open a new one. + if (client is null) + { + try + { + client = new TcpClient(AddressFamily.InterNetwork); + _logger?.LogInformation($"Attempting to establish a connection with {dest}."); + await client.ConnectAsync(dest); + _logger?.LogInformation($"Established connection with {client.Client.RemoteEndPoint as IPEndPoint}"); + } + catch (Exception e) + { + _logger?.LogError($"Failed to connect to remote host: {e.Message}"); + return false; + } + _connections.Add(client); + } + // Write the packet to the client's NetworkStream. + try + { + await client.GetStream().WriteAsync(RoveCommUtils.PackPacket(packet), cancelToken); + } + catch (Exception e) + { + _logger?.LogError($"Failed to send TCP packet: {e.Message}"); + return false; + } + _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + return true; + } + public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => + await SendAsync(packet, ip, Port, cancelToken); + + /// + /// Internal method for reading packets off the network and triggering attached callbacks. + /// + public async Task ReceiveAndCallback(CancellationToken cancelToken = default) + { + if (!Running) + { + throw new RoveCommException("Failed to read TCP packet: RoveComm TCP not started."); + } + + _removeDisconnectedClients(); + + foreach (var connection in _connections) + { + try + { + // Get a reference to the stream managed by the TcpClient. + NetworkStream stream = connection.GetStream(); + // Quit reading if no new data is received after 30 seconds. + stream.ReadTimeout = 30_000; + // Create byte buffer with max packet size. + byte[] readBuf = new byte[RoveCommConsts.HeaderSize + RoveCommConsts.MaxDataSize]; + // Read header. + var headerBuf = readBuf.AsMemory(0, RoveCommConsts.HeaderSize); + int bytesRead = await stream.ReadAsync(headerBuf, cancelToken); + if (bytesRead == 0) + { + return; + } + RoveCommHeader header = RoveCommUtils.ParseHeader(readBuf); + RoveCommDataType dataType = RoveCommUtils.ParseDataType(header.DataType); + int dataTypeSize = RoveCommUtils.DataTypeSize(dataType); + int dataSize = header.DataCount * dataTypeSize; + var dataBuf = readBuf.AsMemory(RoveCommConsts.HeaderSize, dataSize); + await stream.ReadAsync(dataBuf, cancelToken); + // Parse packet and trigger callbacks. + int packetSize = RoveCommConsts.HeaderSize + dataSize; + var packetBuf = readBuf.AsMemory(0, packetSize); + switch (dataType) + { + case RoveCommDataType.INT8_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.UINT8_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.INT16_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.UINT16_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.INT32_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.UINT32_T: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.FLOAT: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; + } + _logger?.LogInformation($"TCP: Received RoveCommPacket with DataID {header.DataID}, DataCount {header.DataCount}, and DataType {dataType}."); + } + // RoveComm couldn't parse something: + catch (RoveCommException e) + { + _logger?.LogError($"Failed to read TCP packet: {e.Message}"); + } + // Network problems: + catch (Exception e) + { + _logger?.LogError($"Failed to receive TCP data: {e.Message}"); + } + } + } + + + /// + /// Trigger attached callbacks for the given RoveCommPacket. + /// + /// The RoveCommPacket to pass to the callbacks. + /// Thrown if the type was invalid. + public void ProcessPacket(RoveCommPacket packet) + { + switch (packet) + { + case RoveCommPacket p: _processPacket(_callbacksInt8, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt8, p); break; + case RoveCommPacket p: _processPacket(_callbacksInt16, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt16, p); break; + case RoveCommPacket p: _processPacket(_callbacksInt32, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt32, p); break; + case RoveCommPacket p: _processPacket(_callbacksFloat, p); break; + case RoveCommPacket p: _processPacket(_callbacksDouble, p); break; + case RoveCommPacket p: _processPacket(_callbacksChar, p); break; + default: throw new RoveCommException("Failed to process RoveCommPacket: invalid data type."); + } + } + private void _processPacket(Dictionary> callbacks, RoveCommPacket packet) + { + if (callbacks.ContainsKey(packet.DataID)) + { + callbacks[packet.DataID].Invoke(packet); + } + // Data ID 0 means subscribe to all packets. + if (callbacks.ContainsKey(0)) + { + callbacks[0].Invoke(packet); + } + } + + /// + /// Attach the given callback to be triggered when a RoveCommPacket with the given DataID is received. + /// + /// The DataID to listen for. + /// The function to call when the DataID is received. + /// Thrown if the type was invalid. + public void On(int dataId, RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _addCallback(_callbacksInt8, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt8, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksInt16, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt16, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksInt32, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt32, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksFloat, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksDouble, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksChar, dataId, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _addCallback(Dictionary> callbacks, int dataId, RoveCommCallback handler) + { + if (!callbacks.ContainsKey(dataId)) + { + callbacks.Add(dataId, new RoveCommEmitter()); + } + + callbacks[dataId].Notifier += handler; + } + + /// + /// Clear the given callback from all DataID's. + /// + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _clearCallback(_callbacksInt8, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt8, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt16, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt16, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt32, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt32, h); break; + case RoveCommCallback h: _clearCallback(_callbacksFloat, h); break; + case RoveCommCallback h: _clearCallback(_callbacksDouble, h); break; + case RoveCommCallback h: _clearCallback(_callbacksChar, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _clearCallback(Dictionary> callbacks, RoveCommCallback handler) + { + foreach (int dataId in callbacks.Keys) + { + callbacks[dataId].Notifier -= handler; + } + } + + /// + /// Clear the given callback from the given DataID. + /// + /// The DataID to remove the callback from. + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(int dataId, RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _clearCallback(_callbacksInt8, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt8, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt16, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt16, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt32, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt32, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksFloat, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksDouble, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksChar, dataId, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _clearCallback(Dictionary> callbacks, int dataId, RoveCommCallback handler) + { + if (!callbacks.ContainsKey(dataId)) + { + callbacks[dataId].Notifier -= handler; + } + } + + public void Dispose() + { + Stop(); + } +} diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs new file mode 100644 index 0000000..c0eb28c --- /dev/null +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -0,0 +1,354 @@ +using System.Diagnostics; +using System.Net; +using System.Net.Sockets; +using Basestation_Software.Models.RoveComm; + +namespace Basestation_Software.Web.Core.Services.RoveComm; + +public class RoveCommUDP : IDisposable +{ + public bool Running { get; private set; } + public int Port { get; private set; } + private UdpClient? _UDPServer; + + private readonly ILogger? _logger; + + private class RoveCommEmitter + { + public event RoveCommCallback? Notifier; + public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet.Data); + } + + private readonly Dictionary> _callbacksInt8 = []; + private readonly Dictionary> _callbacksUInt8 = []; + private readonly Dictionary> _callbacksInt16 = []; + private readonly Dictionary> _callbacksUInt16 = []; + private readonly Dictionary> _callbacksInt32 = []; + private readonly Dictionary> _callbacksUInt32 = []; + private readonly Dictionary> _callbacksFloat = []; + private readonly Dictionary> _callbacksDouble = []; + private readonly Dictionary> _callbacksChar = []; + + public RoveCommUDP(int port, ILogger? logger = null) + { + Port = port; + _logger = logger; + } + public RoveCommUDP(ILogger? logger = null) : this(RoveCommConsts.UDPPort, logger) { } + + /// + /// Begin accepting UDP connections and reading packets from the network. + /// + public void Begin(CancellationToken cancelToken) + { + if (Running) + { + _logger?.LogWarning("RoveComm UDP already started."); + return; + } + + // Open new UDP socket. + _logger?.LogInformation($"Starting RoveComm UDP on port {Port}."); + Task.Run(async () => + { + try + { + _UDPServer = new UdpClient(Port, AddressFamily.InterNetwork); + // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; + Running = true; + while (!cancelToken.IsCancellationRequested) + { + // Read packets and trigger callbacks. + await ReceiveAndCallback(cancelToken); + // Don't hog the async queue. + await Task.Yield(); + } + } + catch (Exception e) + { + _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); + } + finally + { + // Close UDP socket. + Stop(); + } + }, cancelToken); + } + + public void Stop() + { + if (!Running) + { + _logger?.LogWarning("RoveComm UDP already stopped."); + return; + } + + Running = false; + // Close UDP socket; + try + { + _UDPServer!.Close(); + } + catch (Exception e) + { + _logger?.LogError($"Something went wrong closing RoveComm UDP: {e.Message}"); + return; + } + _UDPServer = null; + _logger?.LogInformation("Closed RoveComm UDP."); + } + + /// + /// Send a RoveCommPacket to the given IP and port. + /// + /// The RoveCommPacket to send. + /// The IP to send to. + /// The port to send to. + /// True if the packet was sent successfully. + /// + public bool Send(RoveCommPacket packet, string ip, int port) + { + if (!Running) + { + throw new RoveCommException("Failed to send UDP packet: RoveComm UDP not started."); + } + + var dest = new IPEndPoint(IPAddress.Parse(ip), port); + try + { + int bytesSent = _UDPServer!.Send(RoveCommUtils.PackPacket(packet), dest); + int expected = RoveCommConsts.HeaderSize + packet.DataCount * RoveCommUtils.DataTypeSize(packet.DataType); + if (bytesSent != expected) + { + _logger?.LogError($"Failed to send UDP packet: {bytesSent} of {expected} bytes sent."); + return false; + } + } + catch (Exception e) + { + _logger?.LogError($"Failed to send UDP packet: {e.Message}"); + return false; + } + _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; + _logger?.LogInformation($"Sent from port: {localPort}."); + return true; + } + public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); + + /// + /// Send a RoveCommPacket to the given IP and port asynchronously. + /// + /// The RoveCommPacket to send. + /// The IP to send to. + /// The port to send to. + /// True if the packet was sent successfully. + /// + public async Task SendAsync(RoveCommPacket packet, string ip, int port, CancellationToken cancelToken = default) + { + if (!Running) + { + throw new RoveCommException("Failed to send UDP packet: RoveComm UDP not started."); + } + + var dest = new IPEndPoint(IPAddress.Parse(ip), port); + try + { + int bytesSent = await _UDPServer!.SendAsync(RoveCommUtils.PackPacket(packet), dest, cancelToken); + int expected = RoveCommConsts.HeaderSize + packet.DataCount * RoveCommUtils.DataTypeSize(packet.DataType); + if (bytesSent != expected) + { + _logger?.LogError($"Failed to send UDP packet: {bytesSent} of {expected} bytes sent."); + return false; + } + } + catch (Exception e) + { + _logger?.LogError($"Failed to send UDP packet: {e.Message}"); + return false; + } + _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + return true; + } + public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => + await SendAsync(packet, ip, Port, cancelToken); + + /// + /// Internal method for reading packets off the network and triggering attached callbacks. + /// + public async Task ReceiveAndCallback(CancellationToken cancelToken) + { + if (!Running) + { + throw new RoveCommException("Failed to read UDP packet: RoveComm UDP not started."); + } + + try + { + var result = await _UDPServer!.ReceiveAsync(cancelToken); + IPEndPoint fromIP = result.RemoteEndPoint; + byte[] data = result.Buffer; + if (result.Buffer.Length < RoveCommConsts.HeaderSize) + { + return; + } + + RoveCommHeader header = RoveCommUtils.ParseHeader(data); + switch (RoveCommUtils.ParseDataType(header.DataType)) + { + case RoveCommDataType.INT8_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.UINT8_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.INT16_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.UINT16_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.INT32_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.UINT32_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.FLOAT: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; + } + _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID}, DataCount {header.DataCount}, and DataType {header.DataType}."); + } + // RoveComm couldn't parse something: + catch (RoveCommException e) + { + _logger?.LogError($"Failed to read UDP packet: {e.Message}"); + } + // Network problems: + catch (Exception e) + { + _logger?.LogError($"Failed to receive UDP data: {e.Message}"); + } + } + + /// + /// Trigger attached callbacks for the given RoveCommPacket. + /// + /// The RoveCommPacket to pass to the callbacks. + /// Thrown if the type was invalid. + public void ProcessPacket(RoveCommPacket packet) + { + switch (packet) + { + case RoveCommPacket p: _processPacket(_callbacksInt8, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt8, p); break; + case RoveCommPacket p: _processPacket(_callbacksInt16, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt16, p); break; + case RoveCommPacket p: _processPacket(_callbacksInt32, p); break; + case RoveCommPacket p: _processPacket(_callbacksUInt32, p); break; + case RoveCommPacket p: _processPacket(_callbacksFloat, p); break; + case RoveCommPacket p: _processPacket(_callbacksDouble, p); break; + case RoveCommPacket p: _processPacket(_callbacksChar, p); break; + default: throw new RoveCommException("Failed to process RoveCommPacket: invalid data type."); + } + } + private void _processPacket(Dictionary> callbacks, RoveCommPacket packet) + { + if (callbacks.ContainsKey(packet.DataID)) + { + callbacks[packet.DataID].Invoke(packet); + } + // Data ID 0 means subscribe to all packets. + if (callbacks.ContainsKey(0)) + { + callbacks[0].Invoke(packet); + } + } + + /// + /// Attach the given callback to be triggered when a RoveCommPacket with the given DataID is received. + /// + /// The DataID to listen for. + /// The function to call when the DataID is received. + /// Thrown if the type was invalid. + public void On(int dataId, RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _addCallback(_callbacksInt8, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt8, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksInt16, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt16, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksInt32, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksUInt32, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksFloat, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksDouble, dataId, h); break; + case RoveCommCallback h: _addCallback(_callbacksChar, dataId, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _addCallback(Dictionary> callbacks, int dataId, RoveCommCallback handler) + { + if (!callbacks.ContainsKey(dataId)) + { + callbacks.Add(dataId, new RoveCommEmitter()); + } + + callbacks[dataId].Notifier += handler; + } + + + /// + /// Clear the given callback from all DataID's. + /// + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _clearCallback(_callbacksInt8, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt8, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt16, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt16, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt32, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt32, h); break; + case RoveCommCallback h: _clearCallback(_callbacksFloat, h); break; + case RoveCommCallback h: _clearCallback(_callbacksDouble, h); break; + case RoveCommCallback h: _clearCallback(_callbacksChar, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _clearCallback(Dictionary> callbacks, RoveCommCallback handler) + { + foreach (int dataId in callbacks.Keys) + { + callbacks[dataId].Notifier -= handler; + } + } + + /// + /// Clear the given callback from the given DataID. + /// + /// The DataID to remove the callback from. + /// The callback to remove. + /// Thrown if the type was invalid. + public void Clear(int dataId, RoveCommCallback handler) + { + switch (handler) + { + case RoveCommCallback h: _clearCallback(_callbacksInt8, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt8, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt16, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt16, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksInt32, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksUInt32, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksFloat, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksDouble, dataId, h); break; + case RoveCommCallback h: _clearCallback(_callbacksChar, dataId, h); break; + default: throw new RoveCommException("Failed to add callback: invalid data type."); + } + } + private void _clearCallback(Dictionary> callbacks, int dataId, RoveCommCallback handler) + { + if (!callbacks.ContainsKey(dataId)) + { + callbacks[dataId].Notifier -= handler; + } + } + + public void Dispose() + { + Stop(); + } +} \ No newline at end of file diff --git a/Basestation_Software.Web/Core/_Imports.razor b/Basestation_Software.Web/Core/_Imports.razor index 7d90958..48a3dc5 100644 --- a/Basestation_Software.Web/Core/_Imports.razor +++ b/Basestation_Software.Web/Core/_Imports.razor @@ -10,9 +10,11 @@ @using Basestation_Software.Web @using Basestation_Software.Web.Core @using Basestation_Software.Web.Core.Services +@using Basestation_Software.Web.Core.Services.RoveComm @using Basestation_Software.Web.Core.Components @using Basestation_Software.Models.Geospatial @using Basestation_Software.Models.Timers +@using Basestation_Software.Models.RoveComm @using Radzen @using Radzen.Blazor @using Blazored.Toast @@ -22,4 +24,5 @@ @using LeafletForBlazor @inject CookieService _CookieService -@inject IToastService _ToastService \ No newline at end of file +@inject IToastService _ToastService +@* @inject RoveCommService RoveComm *@ \ No newline at end of file diff --git a/Basestation_Software.Web/Program.cs b/Basestation_Software.Web/Program.cs index 8bc2503..9ebd997 100644 --- a/Basestation_Software.Web/Program.cs +++ b/Basestation_Software.Web/Program.cs @@ -1,5 +1,6 @@ using Basestation_Software.Web.Core; using Basestation_Software.Web.Core.Services; +using Basestation_Software.Web.Core.Services.RoveComm; using Blazored.Toast; using Radzen; @@ -24,6 +25,8 @@ builder.Services.AddSingleton(); builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService((sp) => sp.GetRequiredService()); builder.Services.AddBlazoredToast(); From dc949a4030bab0f7e579b823c27089b948128dd6 Mon Sep 17 00:00:00 2001 From: OcelotEmpire Date: Sun, 22 Sep 2024 03:46:40 -0500 Subject: [PATCH 07/15] improved log messages, fixed object lifetime bugs, and added some utility functions --- .../RoveComm/RoveCommManifest.cs | 1 + .../RoveComm/RoveCommUtils.cs | 48 ++++- .../Core/Components/RoveCommExample.razor | 23 ++- .../Core/Services/RoveComm/RoveCommService.cs | 177 ++++++------------ .../Core/Services/RoveComm/RoveCommTCP.cs | 29 ++- .../Core/Services/RoveComm/RoveCommUDP.cs | 46 ++--- 6 files changed, 162 insertions(+), 162 deletions(-) diff --git a/Basestation_Software.Models/RoveComm/RoveCommManifest.cs b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs index abc2f33..05eed85 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommManifest.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs @@ -7,6 +7,7 @@ public static class RoveCommConsts public static readonly int TCPPort = 12000; public static readonly int HeaderSize = 6; public static readonly int MaxDataSize = 65535 / 3; + public static readonly int UpdateRate = 100; // milliseconds } public enum RoveCommDataType diff --git a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs index 2b562a5..73a4b46 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs @@ -96,16 +96,46 @@ public static RoveCommDataType DataTypeFromType(Type type) }; } + /// + /// Query a board's info form the Manifest by its name. + /// + /// The name of the board. + /// The RoveCommBoardDesc to fill in with informaiton, if the board is found. + /// True if the board was found in the Manifest. + public static bool FindBoardByName(string boardName, out RoveCommBoardDesc? boardDesc) + { + return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc); + } - /// - /// Read a RoveCommPacket header from the given byte buffer. - /// - /// The byte buffer to read; assumed to be in Big Endian. - /// The parsed RoveCommHeader. - /// - /// Thrown if a RoveCommHeader could not be read from the given buffer. - /// - public static RoveCommHeader ParseHeader(ReadOnlySpan data) + /// + /// Query board and packet info from the Manifest by their names. + /// + /// The name of the board. + /// The name of the packet. + /// The RoveCommBoardDesc to fill in with information, if the board is found. + /// The RoveCommPacketDesc to fill in with information, if the packet is found. + /// True only if both the board and the packet were found in the manifest. + public static bool FindByBoardAndDataID(string boardName, string dataIdString, out RoveCommBoardDesc? boardDesc, out RoveCommPacketDesc? packetDesc) + { + packetDesc = null; + return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc) + && ( + boardDesc.Commands.TryGetValue(dataIdString, out packetDesc) + || boardDesc.Telemetry.TryGetValue(dataIdString, out packetDesc) + || boardDesc.Errors.TryGetValue(dataIdString, out packetDesc) + ); + } + + + /// + /// Read a RoveCommPacket header from the given byte buffer. + /// + /// The byte buffer to read; assumed to be in Big Endian. + /// The parsed RoveCommHeader. + /// + /// Thrown if a RoveCommHeader could not be read from the given buffer. + /// + public static RoveCommHeader ParseHeader(ReadOnlySpan data) { if (data.Length < RoveCommConsts.HeaderSize) { diff --git a/Basestation_Software.Web/Core/Components/RoveCommExample.razor b/Basestation_Software.Web/Core/Components/RoveCommExample.razor index 28b486c..4ce01b1 100644 --- a/Basestation_Software.Web/Core/Components/RoveCommExample.razor +++ b/Basestation_Software.Web/Core/Components/RoveCommExample.razor @@ -9,9 +9,10 @@
- - - + + + +

@_message

@if (!_dataLoaded) @@ -46,8 +47,14 @@ protected override async Task OnInitializedAsync() { - // Subscribe to all DataIDs. + // Subscribe to all DataIDs (only for INT32_T packets). _RoveCommService.On(0, AddMessage); + _RoveCommService.On("Core", "DriveLeftRight", async (payload) => + { + _messages.Add("Received DriveLeftRight packet."); + _dataLoaded = true; + await InvokeAsync(StateHasChanged); + }); await InvokeAsync(StateHasChanged); } @@ -83,6 +90,14 @@ _isDisabled = false; } + public async Task SendManifestPacket() + { + await _RoveCommService.SendAsync("Core", "DriveLeftRight", [5.5f, 5.5f]); + // For testing, also send a copy to the loopback address, since RoveComm will send the packet to 192.168.2.110. + RoveCommUtils.FindByBoardAndDataID("Core", "DriveLeftRight", out var board, out var packet); + await _RoveCommService.SendAsync(packet!.DataID, [5.5f, 5.5f], "127.0.0.1"); + } + public async ValueTask DisposeAsync() { _RoveCommService.Clear(AddMessage); diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs index 76dece2..d0780df 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -62,32 +62,20 @@ public void On(int dataId, RoveCommCallback handler) /// public void On(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommPacketDesc? packetDesc; - if (RoveCommManifest.Boards.ContainsKey(boardName)) - { - var boardDesc = RoveCommManifest.Boards[boardName]; - if (boardDesc.Telemetry.ContainsKey(dataIdString)) - { - packetDesc = boardDesc.Telemetry[dataIdString]; - } - else if (boardDesc.Errors.ContainsKey(dataIdString)) - { - packetDesc = boardDesc.Errors[dataIdString]; - } - else - { - throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); - } - } - else - { - throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString}."); + throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); } On(packetDesc.DataID, handler); @@ -114,6 +102,7 @@ public void Clear(int dataId, RoveCommCallback handler) { UDP.Clear(dataId, handler); TCP.Clear(dataId, handler); + _logger.LogInformation($"Unsubscribed from {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}."); } /// @@ -127,36 +116,23 @@ public void Clear(int dataId, RoveCommCallback handler) /// public void Clear(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommPacketDesc? packetDesc; - if (RoveCommManifest.Boards.ContainsKey(boardName)) - { - var boardDesc = RoveCommManifest.Boards[boardName]; - if (boardDesc.Telemetry.ContainsKey(dataIdString)) - { - packetDesc = boardDesc.Telemetry[dataIdString]; - } - else if (boardDesc.Errors.ContainsKey(dataIdString)) - { - packetDesc = boardDesc.Errors[dataIdString]; - } - else - { - throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); - } - } - else - { - throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to unsubscribe from RoveComm: {dataIdString} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString}."); + throw new Exception($"Failed to unsubscribe from RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); } - UDP.Clear(packetDesc.DataID, handler); - TCP.Clear(packetDesc.DataID, handler); + Clear(packetDesc.DataID, handler); } /// @@ -212,37 +188,20 @@ public async Task SendAsync(int dataId, List data, string ip, bool r /// public bool Send(string boardName, string commandName, List data, bool reliable = false) { - RoveCommBoardDesc? boardDesc; - RoveCommPacketDesc? packetDesc; - if (RoveCommManifest.Boards.ContainsKey(boardName)) - { - boardDesc = RoveCommManifest.Boards[boardName]; - if (boardDesc.Commands.ContainsKey(commandName)) - { - packetDesc = boardDesc.Commands[commandName]; - } - else if (boardDesc.Telemetry.ContainsKey(commandName)) - { - packetDesc = boardDesc.Telemetry[commandName]; - } - else if (boardDesc.Errors.ContainsKey(commandName)) - { - packetDesc = boardDesc.Errors[commandName]; - } - else - { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); - } - } - else - { + RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); } + else if (packetDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName}."); + throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); } if (data.Count != packetDesc.DataCount) @@ -266,37 +225,20 @@ public bool Send(string boardName, string commandName, List data, bool rel /// public async Task SendAsync(string boardName, string commandName, List data, bool reliable = false, CancellationToken cancelToken = default) { - RoveCommBoardDesc? boardDesc; - RoveCommPacketDesc? packetDesc; - if (RoveCommManifest.Boards.ContainsKey(boardName)) - { - boardDesc = RoveCommManifest.Boards[boardName]; - if (boardDesc.Commands.ContainsKey(commandName)) - { - packetDesc = boardDesc.Commands[commandName]; - } - else if (boardDesc.Telemetry.ContainsKey(commandName)) - { - packetDesc = boardDesc.Telemetry[commandName]; - } - else if (boardDesc.Errors.ContainsKey(commandName)) - { - packetDesc = boardDesc.Errors[commandName]; - } - else - { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); - } - } - else - { - throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName}."); + throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); } if (data.Count != packetDesc.DataCount) @@ -327,19 +269,18 @@ public async Task SendAsync(string boardName, string commandName, List< var cancel = cts.Token; cts.CancelAfter(timeout); - using (cancel.Register(() => { - Clear(dataId, callback); - })) + try + { + return await promise.Task.WaitAsync(cancel); + } + catch (OperationCanceledException) { - try - { - return await promise.Task.WaitAsync(cancel); - } - catch (OperationCanceledException) - { - return null; - } + return null; } + finally + { + Clear(dataId, callback); + } } public void Subscribe(string boardName) @@ -351,7 +292,7 @@ public void Subscribe(string boardName) } public void SubscribeAll() { - foreach (var (_, board) in RoveCommManifest.Boards) + foreach (var board in RoveCommManifest.Boards.Values) { Send(RoveCommManifest.SystemPackets.SUBSCRIBE, [1], board.IP); } @@ -366,7 +307,7 @@ public void Unubscribe(string boardName) } public void UnsubscribeAll() { - foreach (var (_, board) in RoveCommManifest.Boards) + foreach (var board in RoveCommManifest.Boards.Values) { Send(RoveCommManifest.SystemPackets.UNSUBSCRIBE, [1], board.IP); } @@ -381,7 +322,7 @@ public void Ping(string boardName) } public void PingAll() { - foreach (var (_, board) in RoveCommManifest.Boards) + foreach (var board in RoveCommManifest.Boards.Values) { Send(RoveCommManifest.SystemPackets.PING, [1], board.IP); } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index 6a9330a..e1fbf2a 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -69,18 +69,19 @@ public void Begin(CancellationToken cancelToken) { TcpClient client = await _TCPServer!.AcceptTcpClientAsync(cancelToken); _connections.Add(client); - _logger?.LogError($"Established connection with {client.Client.RemoteEndPoint}"); + _logger?.LogInformation($"Accepted connection with remote: {client.Client.RemoteEndPoint}"); } // Read packets and trigger callbacks. await ReceiveAndCallback(cancelToken); // Don't hog the async queue. - await Task.Yield(); + await Task.Delay(RoveCommConsts.UpdateRate); } } catch (Exception e) { _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); - } + _logger?.LogError(e.StackTrace); + } finally { // Close connections. @@ -204,7 +205,7 @@ public bool Send(RoveCommPacket packet, string ip, int port) _logger?.LogError($"Failed to send TCP packet: {e.Message}"); return false; } - _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); return true; } public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); @@ -255,9 +256,9 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po { _logger?.LogError($"Failed to send TCP packet: {e.Message}"); return false; - } - _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); - return true; + } + _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); + return true; } public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => await SendAsync(packet, ip, Port, cancelToken); @@ -278,6 +279,12 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) { try { + // Check if the client has enough bytes available to read a packet. + // TCP is a stream protocol, so if not enough bytes are available now, more will come in later. + if (connection.Client.Available < RoveCommConsts.HeaderSize) + { + continue; + } // Get a reference to the stream managed by the TcpClient. NetworkStream stream = connection.GetStream(); // Quit reading if no new data is received after 30 seconds. @@ -289,8 +296,10 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) int bytesRead = await stream.ReadAsync(headerBuf, cancelToken); if (bytesRead == 0) { + _logger?.LogWarning("Failed to receive TCP data."); return; } + RoveCommHeader header = RoveCommUtils.ParseHeader(readBuf); RoveCommDataType dataType = RoveCommUtils.ParseDataType(header.DataType); int dataTypeSize = RoveCommUtils.DataTypeSize(dataType); @@ -312,7 +321,7 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; } - _logger?.LogInformation($"TCP: Received RoveCommPacket with DataID {header.DataID}, DataCount {header.DataCount}, and DataType {dataType}."); + _logger?.LogInformation($"TCP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {connection.Client.RemoteEndPoint as IPEndPoint}."); } // RoveComm couldn't parse something: catch (RoveCommException e) @@ -417,9 +426,9 @@ public void Clear(RoveCommCallback handler) } private void _clearCallback(Dictionary> callbacks, RoveCommCallback handler) { - foreach (int dataId in callbacks.Keys) + foreach (var emitter in callbacks.Values) { - callbacks[dataId].Notifier -= handler; + emitter.Notifier -= handler; } } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs index c0eb28c..c3c336b 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -61,12 +61,13 @@ public void Begin(CancellationToken cancelToken) // Read packets and trigger callbacks. await ReceiveAndCallback(cancelToken); // Don't hog the async queue. - await Task.Yield(); - } - } + await Task.Delay(RoveCommConsts.UpdateRate); + } + } catch (Exception e) { _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); + _logger?.LogError(e.StackTrace); } finally { @@ -129,11 +130,11 @@ public bool Send(RoveCommPacket packet, string ip, int port) { _logger?.LogError($"Failed to send UDP packet: {e.Message}"); return false; - } - _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); - int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; - _logger?.LogInformation($"Sent from port: {localPort}."); - return true; + } + _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); + // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; + // _logger?.LogInformation($"Sent from port: {localPort}."); + return true; } public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); @@ -168,7 +169,7 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po _logger?.LogError($"Failed to send UDP packet: {e.Message}"); return false; } - _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID}, DataCount {packet.DataCount}, and DataType {packet.DataType}."); + _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); return true; } public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => @@ -186,16 +187,19 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) try { + // We still want to process datagrams which are too small to parse; we just discard them. + if (_UDPServer!.Available == 0) + { + return; + } + var result = await _UDPServer!.ReceiveAsync(cancelToken); IPEndPoint fromIP = result.RemoteEndPoint; byte[] data = result.Buffer; - if (result.Buffer.Length < RoveCommConsts.HeaderSize) - { - return; - } - + // If there aren't enough bytes to parse the header, the error will be caught and logged. RoveCommHeader header = RoveCommUtils.ParseHeader(data); - switch (RoveCommUtils.ParseDataType(header.DataType)) + RoveCommDataType dataType = RoveCommUtils.ParseDataType(header.DataType); + switch (dataType) { case RoveCommDataType.INT8_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; case RoveCommDataType.UINT8_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; @@ -207,10 +211,10 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; } - _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID}, DataCount {header.DataCount}, and DataType {header.DataType}."); - } - // RoveComm couldn't parse something: - catch (RoveCommException e) + _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {fromIP}."); + } + // RoveComm couldn't parse something: + catch (RoveCommException e) { _logger?.LogError($"Failed to read UDP packet: {e.Message}"); } @@ -311,9 +315,9 @@ public void Clear(RoveCommCallback handler) } private void _clearCallback(Dictionary> callbacks, RoveCommCallback handler) { - foreach (int dataId in callbacks.Keys) + foreach (var emitter in callbacks.Values) { - callbacks[dataId].Notifier -= handler; + emitter.Notifier -= handler; } } From cfa5a3e341b7b3d03ff3766a4944ece66b358be2 Mon Sep 17 00:00:00 2001 From: OcelotEmpire Date: Sun, 22 Sep 2024 09:16:48 +0000 Subject: [PATCH 08/15] visual studio was using tabs >:( --- .../RoveComm/RoveCommManifest.cs | 2 +- .../RoveComm/RoveCommUtils.cs | 80 +++++++-------- .../Core/Services/RoveComm/RoveCommService.cs | 97 ++++++++++--------- .../Core/Services/RoveComm/RoveCommTCP.cs | 24 ++--- .../Core/Services/RoveComm/RoveCommUDP.cs | 40 ++++---- 5 files changed, 122 insertions(+), 121 deletions(-) diff --git a/Basestation_Software.Models/RoveComm/RoveCommManifest.cs b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs index 05eed85..33e0bde 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommManifest.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommManifest.cs @@ -7,7 +7,7 @@ public static class RoveCommConsts public static readonly int TCPPort = 12000; public static readonly int HeaderSize = 6; public static readonly int MaxDataSize = 65535 / 3; - public static readonly int UpdateRate = 100; // milliseconds + public static readonly int UpdateRate = 100; // milliseconds } public enum RoveCommDataType diff --git a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs index 73a4b46..ed86ca3 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs @@ -96,46 +96,46 @@ public static RoveCommDataType DataTypeFromType(Type type) }; } - /// - /// Query a board's info form the Manifest by its name. - /// - /// The name of the board. - /// The RoveCommBoardDesc to fill in with informaiton, if the board is found. - /// True if the board was found in the Manifest. - public static bool FindBoardByName(string boardName, out RoveCommBoardDesc? boardDesc) - { - return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc); - } - - /// - /// Query board and packet info from the Manifest by their names. - /// - /// The name of the board. - /// The name of the packet. - /// The RoveCommBoardDesc to fill in with information, if the board is found. - /// The RoveCommPacketDesc to fill in with information, if the packet is found. - /// True only if both the board and the packet were found in the manifest. - public static bool FindByBoardAndDataID(string boardName, string dataIdString, out RoveCommBoardDesc? boardDesc, out RoveCommPacketDesc? packetDesc) - { - packetDesc = null; - return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc) - && ( - boardDesc.Commands.TryGetValue(dataIdString, out packetDesc) - || boardDesc.Telemetry.TryGetValue(dataIdString, out packetDesc) - || boardDesc.Errors.TryGetValue(dataIdString, out packetDesc) - ); - } - - - /// - /// Read a RoveCommPacket header from the given byte buffer. - /// - /// The byte buffer to read; assumed to be in Big Endian. - /// The parsed RoveCommHeader. - /// - /// Thrown if a RoveCommHeader could not be read from the given buffer. - /// - public static RoveCommHeader ParseHeader(ReadOnlySpan data) + /// + /// Query a board's info form the Manifest by its name. + /// + /// The name of the board. + /// The RoveCommBoardDesc to fill in with informaiton, if the board is found. + /// True if the board was found in the Manifest. + public static bool FindBoardByName(string boardName, out RoveCommBoardDesc? boardDesc) + { + return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc); + } + + /// + /// Query board and packet info from the Manifest by their names. + /// + /// The name of the board. + /// The name of the packet. + /// The RoveCommBoardDesc to fill in with information, if the board is found. + /// The RoveCommPacketDesc to fill in with information, if the packet is found. + /// True only if both the board and the packet were found in the manifest. + public static bool FindByBoardAndDataID(string boardName, string dataIdString, out RoveCommBoardDesc? boardDesc, out RoveCommPacketDesc? packetDesc) + { + packetDesc = null; + return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc) + && ( + boardDesc.Commands.TryGetValue(dataIdString, out packetDesc) + || boardDesc.Telemetry.TryGetValue(dataIdString, out packetDesc) + || boardDesc.Errors.TryGetValue(dataIdString, out packetDesc) + ); + } + + + /// + /// Read a RoveCommPacket header from the given byte buffer. + /// + /// The byte buffer to read; assumed to be in Big Endian. + /// The parsed RoveCommHeader. + /// + /// Thrown if a RoveCommHeader could not be read from the given buffer. + /// + public static RoveCommHeader ParseHeader(ReadOnlySpan data) { if (data.Length < RoveCommConsts.HeaderSize) { diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs index d0780df..4037267 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -62,17 +62,17 @@ public void On(int dataId, RoveCommCallback handler) /// public void On(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); - if (boardDesc is null) - { - throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); - } - else if (packetDesc is null) - { - throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); @@ -102,7 +102,7 @@ public void Clear(int dataId, RoveCommCallback handler) { UDP.Clear(dataId, handler); TCP.Clear(dataId, handler); - _logger.LogInformation($"Unsubscribed from {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}."); + _logger.LogInformation($"Unsubscribed from {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}."); } /// @@ -116,17 +116,17 @@ public void Clear(int dataId, RoveCommCallback handler) /// public void Clear(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); - if (boardDesc is null) - { - throw new Exception($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); - } - else if (packetDesc is null) - { - throw new Exception($"Failed to unsubscribe from RoveComm: {dataIdString} not found for {boardName} Board."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to unsubscribe from RoveComm: {dataIdString} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { throw new Exception($"Failed to unsubscribe from RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); @@ -188,15 +188,15 @@ public async Task SendAsync(int dataId, List data, string ip, bool r /// public bool Send(string boardName, string commandName, List data, bool reliable = false) { - RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); if (boardDesc is null) - { + { throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); } - else if (packetDesc is null) - { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); - } + else if (packetDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) @@ -225,17 +225,17 @@ public bool Send(string boardName, string commandName, List data, bool rel /// public async Task SendAsync(string boardName, string commandName, List data, bool reliable = false, CancellationToken cancelToken = default) { - RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); - if (boardDesc is null) - { - throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); - } - else if (packetDesc is null) - { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); - } - - RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); + RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + } + + RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); @@ -258,10 +258,11 @@ public async Task SendAsync(string boardName, string commandName, List< public async Task?> Listen(int dataId, int timeout = 30_000) { var promise = new TaskCompletionSource>(); - RoveCommCallback callback = async (payload) => { - promise.SetResult(payload); - await Task.CompletedTask; - }; + RoveCommCallback callback = async (payload) => + { + promise.SetResult(payload); + await Task.CompletedTask; + }; On(dataId, callback); @@ -277,10 +278,10 @@ public async Task SendAsync(string boardName, string commandName, List< { return null; } - finally - { - Clear(dataId, callback); - } + finally + { + Clear(dataId, callback); + } } public void Subscribe(string boardName) diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index e1fbf2a..0897ba4 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -80,8 +80,8 @@ public void Begin(CancellationToken cancelToken) catch (Exception e) { _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); - _logger?.LogError(e.StackTrace); - } + _logger?.LogError(e.StackTrace); + } finally { // Close connections. @@ -256,9 +256,9 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po { _logger?.LogError($"Failed to send TCP packet: {e.Message}"); return false; - } - _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); - return true; + } + _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); + return true; } public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => await SendAsync(packet, ip, Port, cancelToken); @@ -279,12 +279,12 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) { try { - // Check if the client has enough bytes available to read a packet. - // TCP is a stream protocol, so if not enough bytes are available now, more will come in later. - if (connection.Client.Available < RoveCommConsts.HeaderSize) - { - continue; - } + // Check if the client has enough bytes available to read a packet. + // TCP is a stream protocol, so if not enough bytes are available now, more will come in later. + if (connection.Client.Available < RoveCommConsts.HeaderSize) + { + continue; + } // Get a reference to the stream managed by the TcpClient. NetworkStream stream = connection.GetStream(); // Quit reading if no new data is received after 30 seconds. @@ -296,7 +296,7 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) int bytesRead = await stream.ReadAsync(headerBuf, cancelToken); if (bytesRead == 0) { - _logger?.LogWarning("Failed to receive TCP data."); + _logger?.LogWarning("Failed to receive TCP data."); return; } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs index c3c336b..72c6b08 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -49,7 +49,7 @@ public void Begin(CancellationToken cancelToken) // Open new UDP socket. _logger?.LogInformation($"Starting RoveComm UDP on port {Port}."); - Task.Run(async () => + Task.Run(async () => { try { @@ -62,12 +62,12 @@ public void Begin(CancellationToken cancelToken) await ReceiveAndCallback(cancelToken); // Don't hog the async queue. await Task.Delay(RoveCommConsts.UpdateRate); - } - } + } + } catch (Exception e) { _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); - _logger?.LogError(e.StackTrace); + _logger?.LogError(e.StackTrace); } finally { @@ -130,11 +130,11 @@ public bool Send(RoveCommPacket packet, string ip, int port) { _logger?.LogError($"Failed to send UDP packet: {e.Message}"); return false; - } - _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); - // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; - // _logger?.LogInformation($"Sent from port: {localPort}."); - return true; + } + _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); + // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; + // _logger?.LogInformation($"Sent from port: {localPort}."); + return true; } public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); @@ -187,18 +187,18 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) try { - // We still want to process datagrams which are too small to parse; we just discard them. - if (_UDPServer!.Available == 0) - { - return; - } + // We still want to process datagrams which are too small to parse; we just discard them. + if (_UDPServer!.Available == 0) + { + return; + } var result = await _UDPServer!.ReceiveAsync(cancelToken); IPEndPoint fromIP = result.RemoteEndPoint; byte[] data = result.Buffer; - // If there aren't enough bytes to parse the header, the error will be caught and logged. + // If there aren't enough bytes to parse the header, the error will be caught and logged. RoveCommHeader header = RoveCommUtils.ParseHeader(data); - RoveCommDataType dataType = RoveCommUtils.ParseDataType(header.DataType); + RoveCommDataType dataType = RoveCommUtils.ParseDataType(header.DataType); switch (dataType) { case RoveCommDataType.INT8_T: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; @@ -211,10 +211,10 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; } - _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {fromIP}."); - } - // RoveComm couldn't parse something: - catch (RoveCommException e) + _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {fromIP}."); + } + // RoveComm couldn't parse something: + catch (RoveCommException e) { _logger?.LogError($"Failed to read UDP packet: {e.Message}"); } From 677cc7ef364c77ba93034a378b55078b6dcfa5bd Mon Sep 17 00:00:00 2001 From: Adam Klassen Date: Sun, 22 Sep 2024 18:16:17 +0000 Subject: [PATCH 09/15] fixed logger warnings --- .../Core/Services/RoveComm/RoveCommService.cs | 4 +- .../Core/Services/RoveComm/RoveCommTCP.cs | 39 +++++++++---------- .../Core/Services/RoveComm/RoveCommUDP.cs | 29 +++++++------- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs index 4037267..8a440e6 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -48,7 +48,7 @@ public void On(int dataId, RoveCommCallback handler) { TCP.On(dataId, handler); UDP.On(dataId, handler); - _logger.LogInformation($"Subscribed to {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}"); + _logger.LogInformation("Subscribed to {DataID} with type {DataType}.", dataId, RoveCommUtils.DataTypeFromType(typeof(T))); } /// @@ -102,7 +102,7 @@ public void Clear(int dataId, RoveCommCallback handler) { UDP.Clear(dataId, handler); TCP.Clear(dataId, handler); - _logger.LogInformation($"Unsubscribed from {dataId} with type {RoveCommUtils.DataTypeFromType(typeof(T))}."); + _logger.LogInformation("Unsubscribed from {DataID} with type {DataType}.", dataId, RoveCommUtils.DataTypeFromType(typeof(T))); } /// diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index 0897ba4..636f0a0 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -46,7 +46,7 @@ public void Begin(CancellationToken cancelToken) return; } // Begin listening for TCP connection requests. Allow up to 10 pending requests at once. - _logger?.LogInformation($"Starting RoveComm TCP on port {Port}."); + _logger?.LogInformation("Starting RoveComm TCP on port {Port}.", Port); _TCPServer = new TcpListener(IPAddress.Any, Port); try { @@ -54,7 +54,7 @@ public void Begin(CancellationToken cancelToken) } catch (Exception e) { - _logger?.LogError($"Failed to start RoveComm TCP: {e.Message}"); + _logger?.LogError(e, "Failed to start RoveComm TCP:"); return; } Running = true; @@ -69,7 +69,7 @@ public void Begin(CancellationToken cancelToken) { TcpClient client = await _TCPServer!.AcceptTcpClientAsync(cancelToken); _connections.Add(client); - _logger?.LogInformation($"Accepted connection with remote: {client.Client.RemoteEndPoint}"); + _logger?.LogInformation("Accepted connection from {Remote}.", client.Client.RemoteEndPoint); } // Read packets and trigger callbacks. await ReceiveAndCallback(cancelToken); @@ -79,8 +79,7 @@ public void Begin(CancellationToken cancelToken) } catch (Exception e) { - _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); - _logger?.LogError(e.StackTrace); + _logger?.LogError(e, "An exception occurred in RoveComm UDP:"); } finally { @@ -115,7 +114,7 @@ public void Stop() } catch (Exception e) { - _logger?.LogError($"Something went wrong closing RoveComm TCP: {e.Message}"); + _logger?.LogError(e, "Something went wrong closing RoveComm TCP:"); return; } _logger?.LogInformation("Closed RoveComm TCP."); @@ -147,7 +146,7 @@ private void _removeDisconnectedClients() { if (!connection.Connected) { - _logger?.LogInformation($"Disconnected from {connection.Client.RemoteEndPoint as IPEndPoint}."); + _logger?.LogInformation("Disconnected from {Remote}.", connection.Client.RemoteEndPoint as IPEndPoint); return true; } else @@ -183,14 +182,14 @@ public bool Send(RoveCommPacket packet, string ip, int port) { try { - _logger?.LogInformation($"Attempting to establish a connection with {dest}."); + _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); client = new TcpClient(AddressFamily.InterNetwork); client.Connect(dest); - _logger?.LogInformation($"Established connection with {client.Client.RemoteEndPoint as IPEndPoint}"); + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); } catch (Exception e) { - _logger?.LogError($"Failed to connect to remote host: {e.Message}"); + _logger?.LogError(e, "Failed to connect to remote host:"); return false; } _connections.Add(client); @@ -202,10 +201,10 @@ public bool Send(RoveCommPacket packet, string ip, int port) } catch (Exception e) { - _logger?.LogError($"Failed to send TCP packet: {e.Message}"); + _logger?.LogError(e, "Failed to send TCP packet:"); return false; } - _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); + _logger?.LogInformation("TCP: Sent RoveCommPacket with DataID {DataID} and type {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); return true; } public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); @@ -236,13 +235,13 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po try { client = new TcpClient(AddressFamily.InterNetwork); - _logger?.LogInformation($"Attempting to establish a connection with {dest}."); + _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); await client.ConnectAsync(dest); - _logger?.LogInformation($"Established connection with {client.Client.RemoteEndPoint as IPEndPoint}"); + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); } catch (Exception e) { - _logger?.LogError($"Failed to connect to remote host: {e.Message}"); + _logger?.LogError(e, "Failed to connect to remote host:"); return false; } _connections.Add(client); @@ -254,10 +253,10 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po } catch (Exception e) { - _logger?.LogError($"Failed to send TCP packet: {e.Message}"); + _logger?.LogError(e, "Failed to send TCP packet:"); return false; } - _logger?.LogInformation($"TCP: Sent RoveCommPacket with DataID {packet.DataID} and type {packet.DataType}[{packet.DataCount}] to {dest}."); + _logger?.LogInformation("TCP: Sent RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); return true; } public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => @@ -321,17 +320,17 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(packetBuf.Span)); break; } - _logger?.LogInformation($"TCP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {connection.Client.RemoteEndPoint as IPEndPoint}."); + _logger?.LogInformation("TCP: Received RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] from {Remote}.", header.DataID, dataType, header.DataCount, connection.Client.RemoteEndPoint as IPEndPoint); } // RoveComm couldn't parse something: catch (RoveCommException e) { - _logger?.LogError($"Failed to read TCP packet: {e.Message}"); + _logger?.LogError("Failed to read TCP packet: {Error}", e.Message); } // Network problems: catch (Exception e) { - _logger?.LogError($"Failed to receive TCP data: {e.Message}"); + _logger?.LogError(e, "Failed to receive TCP data:"); } } } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs index 72c6b08..1e9ee58 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -48,7 +48,7 @@ public void Begin(CancellationToken cancelToken) } // Open new UDP socket. - _logger?.LogInformation($"Starting RoveComm UDP on port {Port}."); + _logger?.LogInformation("Starting RoveComm UDP on port {Port}.", Port); Task.Run(async () => { try @@ -66,8 +66,7 @@ public void Begin(CancellationToken cancelToken) } catch (Exception e) { - _logger?.LogError($"An exception occurred in RoveComm UDP: {e.Message}"); - _logger?.LogError(e.StackTrace); + _logger?.LogError(e, "An exception occurred in RoveComm UDP:"); } finally { @@ -93,7 +92,7 @@ public void Stop() } catch (Exception e) { - _logger?.LogError($"Something went wrong closing RoveComm UDP: {e.Message}"); + _logger?.LogError(e, "Something went wrong closing RoveComm UDP:"); return; } _UDPServer = null; @@ -122,18 +121,17 @@ public bool Send(RoveCommPacket packet, string ip, int port) int expected = RoveCommConsts.HeaderSize + packet.DataCount * RoveCommUtils.DataTypeSize(packet.DataType); if (bytesSent != expected) { - _logger?.LogError($"Failed to send UDP packet: {bytesSent} of {expected} bytes sent."); + _logger?.LogError("Failed to send UDP packet: {Sent} of {Expected} bytes sent.", bytesSent, expected); return false; } } catch (Exception e) { - _logger?.LogError($"Failed to send UDP packet: {e.Message}"); + _logger?.LogError(e, "Failed to send UDP packet:"); return false; } - _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); - // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; - // _logger?.LogInformation($"Sent from port: {localPort}."); + + _logger?.LogInformation("UDP: Sent RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); return true; } public bool Send(RoveCommPacket packet, string ip) => Send(packet, ip, Port); @@ -160,16 +158,17 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po int expected = RoveCommConsts.HeaderSize + packet.DataCount * RoveCommUtils.DataTypeSize(packet.DataType); if (bytesSent != expected) { - _logger?.LogError($"Failed to send UDP packet: {bytesSent} of {expected} bytes sent."); + _logger?.LogError("Failed to send UDP packet: {Sent} of {Expected} bytes sent.", bytesSent, expected); return false; } } catch (Exception e) { - _logger?.LogError($"Failed to send UDP packet: {e.Message}"); + _logger?.LogError(e, "Failed to send UDP packet:"); return false; } - _logger?.LogInformation($"UDP: Sent RoveCommPacket with DataID {packet.DataID} and Data {packet.DataType}[{packet.DataCount}] to {dest}."); + + _logger?.LogInformation("UDP: Sent RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); return true; } public async Task SendAsync(RoveCommPacket packet, string ip, CancellationToken cancelToken = default) => @@ -211,17 +210,17 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) case RoveCommDataType.DOUBLE: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; case RoveCommDataType.CHAR: ProcessPacket(RoveCommUtils.ParsePacket(data)); break; } - _logger?.LogInformation($"UDP: Received RoveCommPacket with DataID {header.DataID} and Data {dataType}[{header.DataCount}] from {fromIP}."); + _logger?.LogInformation("UDP: Received RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] from {Remote}.", header.DataID, dataType, header.DataCount, fromIP); } // RoveComm couldn't parse something: catch (RoveCommException e) { - _logger?.LogError($"Failed to read UDP packet: {e.Message}"); + _logger?.LogError("Failed to read UDP packet: {Error}", e.Message); } // Network problems: catch (Exception e) { - _logger?.LogError($"Failed to receive UDP data: {e.Message}"); + _logger?.LogError(e, "Failed to receive UDP data:"); } } From 8bbdeb65111131a878ee46d165c08512e4644767 Mon Sep 17 00:00:00 2001 From: Adam Klassen Date: Mon, 23 Sep 2024 04:27:44 +0000 Subject: [PATCH 10/15] fixed float encoding and added TCP timeout --- .../RoveComm/RoveCommUtils.cs | 2 +- .../Core/Services/RoveComm/RoveCommService.cs | 50 +++++++++++++------ .../Core/Services/RoveComm/RoveCommTCP.cs | 32 ++++++------ .../Core/Services/RoveComm/RoveCommUDP.cs | 19 ++++--- 4 files changed, 65 insertions(+), 38 deletions(-) diff --git a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs index ed86ca3..b86859b 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs @@ -267,7 +267,7 @@ public static RoveCommPacket ParsePacket(ReadOnlySpan data) { for (int i = 0; i < header.DataCount; i++) { - packetData.Add(BinaryPrimitives.ReadUInt32BigEndian(dataBuf.Slice(i * 4, 4))); + packetData.Add(BinaryPrimitives.ReadSingleBigEndian(dataBuf.Slice(i * 4, 4))); } break; diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs index 8a440e6..1dc32ea 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -65,17 +65,17 @@ public void On(string boardName, string dataIdString, RoveCommCallback han RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); if (boardDesc is null) { - throw new Exception($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); + throw new RoveCommException($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); } else if (packetDesc is null) { - throw new Exception($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); + throw new RoveCommException($"Failed to subscribe to RoveComm: {dataIdString} not found for {boardName} Board."); } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); + throw new RoveCommException($"Failed to subscribe to RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); } On(packetDesc.DataID, handler); @@ -119,17 +119,17 @@ public void Clear(string boardName, string dataIdString, RoveCommCallback RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); if (boardDesc is null) { - throw new Exception($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); + throw new RoveCommException($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); } else if (packetDesc is null) { - throw new Exception($"Failed to unsubscribe from RoveComm: {dataIdString} not found for {boardName} Board."); + throw new RoveCommException($"Failed to unsubscribe from RoveComm: {dataIdString} not found for {boardName} Board."); } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to unsubscribe from RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); + throw new RoveCommException($"Failed to unsubscribe from RoveComm: {handlerType} does not match type of {dataIdString} ({packetDesc.DataType})."); } Clear(packetDesc.DataID, handler); @@ -191,22 +191,22 @@ public bool Send(string boardName, string commandName, List data, bool rel RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); if (boardDesc is null) { - throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); } else if (packetDesc is null) { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + throw new RoveCommException($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); + throw new RoveCommException($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); } if (data.Count != packetDesc.DataCount) { - throw new Exception($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); + throw new RoveCommException($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); } return Send(packetDesc.DataID, data, boardDesc.IP, reliable); @@ -228,22 +228,22 @@ public async Task SendAsync(string boardName, string commandName, List< RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); if (boardDesc is null) { - throw new Exception($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); } else if (packetDesc is null) { - throw new Exception($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); + throw new RoveCommException($"Failed to send RoveCommPacket: {commandName} not found for {boardName} Board."); } RoveCommDataType handlerType = RoveCommUtils.DataTypeFromType(typeof(T)); if (packetDesc.DataType != handlerType) { - throw new Exception($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); + throw new RoveCommException($"Failed to send RoveCommPacket: {handlerType} does not match type of {commandName} ({packetDesc.DataType})."); } if (data.Count != packetDesc.DataCount) { - throw new Exception($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); + throw new RoveCommException($"Failed to send RoveCommPacket: incorrect data size to fill {commandName}."); } return await SendAsync(packetDesc.DataID, data, boardDesc.IP, reliable, cancelToken); @@ -284,6 +284,28 @@ public async Task SendAsync(string boardName, string commandName, List< } } + /// + /// Wait asynchronously until the desired packet from the Manifest arrives. + /// + /// The name of the board as shown in the Manifest. + /// The name of the Telemetry or Error message as shown in the Manifest. + /// The number of milliseconds before returning null. + /// The payload if a packet is received within the timeout, null if none. + public async Task?> Listen(string boardName, string dataIdString, int timeout = 30_000) + { + RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + if (boardDesc is null) + { + throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); + } + else if (packetDesc is null) + { + throw new RoveCommException($"Failed to send RoveCommPacket: {dataIdString} not found for {boardName} Board."); + } + + return await Listen(packetDesc.DataID, timeout); + } + public void Subscribe(string boardName) { if (RoveCommManifest.Boards.ContainsKey(boardName)) diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index 636f0a0..d64170e 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -6,6 +6,8 @@ namespace Basestation_Software.Web.Core.Services.RoveComm; public class RoveCommTCP { + public static readonly int ConnectionTimeout = 10_000; + public bool Running { get; private set; } public int Port { get; private set; } private readonly List _connections = []; @@ -180,18 +182,14 @@ public bool Send(RoveCommPacket packet, string ip, int port) // If no existing connection was found, open a new one. if (client is null) { - try - { - _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); - client = new TcpClient(AddressFamily.InterNetwork); - client.Connect(dest); - _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); - } - catch (Exception e) + _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); + client = new TcpClient(AddressFamily.InterNetwork); + if (!client.ConnectAsync(dest).Wait(TimeSpan.FromMilliseconds(ConnectionTimeout))) { - _logger?.LogError(e, "Failed to connect to remote host:"); + _logger?.LogError("Failed to connect to remote host: The operation has timed out."); return false; } + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); _connections.Add(client); } // Write the packet to the client's NetworkStream. @@ -201,7 +199,7 @@ public bool Send(RoveCommPacket packet, string ip, int port) } catch (Exception e) { - _logger?.LogError(e, "Failed to send TCP packet:"); + _logger?.LogError("Failed to send TCP packet: {Error}", e.Message); return false; } _logger?.LogInformation("TCP: Sent RoveCommPacket with DataID {DataID} and type {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); @@ -232,18 +230,18 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po // If no existing connection was found, open a new one. if (client is null) { + client = new TcpClient(AddressFamily.InterNetwork); + _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); try { - client = new TcpClient(AddressFamily.InterNetwork); - _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); - await client.ConnectAsync(dest); - _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); + await client.ConnectAsync(dest).WaitAsync(TimeSpan.FromMilliseconds(ConnectionTimeout), cancelToken); } catch (Exception e) { - _logger?.LogError(e, "Failed to connect to remote host:"); + _logger?.LogError("Failed to connect to remote host: {Error}", e.Message); return false; } + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); _connections.Add(client); } // Write the packet to the client's NetworkStream. @@ -253,7 +251,7 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po } catch (Exception e) { - _logger?.LogError(e, "Failed to send TCP packet:"); + _logger?.LogError("Failed to send TCP packet: {Error}", e.Message); return false; } _logger?.LogInformation("TCP: Sent RoveCommPacket with DataID {DataID} and Data {DataType}[{DataCount}] to {Dest}.", packet.DataID, packet.DataType, packet.DataCount, dest); @@ -330,7 +328,7 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) // Network problems: catch (Exception e) { - _logger?.LogError(e, "Failed to receive TCP data:"); + _logger?.LogError("Failed to receive TCP data: {Error}", e.Message); } } } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs index 1e9ee58..f2ac283 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -49,13 +49,20 @@ public void Begin(CancellationToken cancelToken) // Open new UDP socket. _logger?.LogInformation("Starting RoveComm UDP on port {Port}.", Port); + try + { + _UDPServer = new UdpClient(Port, AddressFamily.InterNetwork); + } + catch (Exception e) + { + _logger?.LogError(e, "Failed to start RoveComm UDP:"); + return; + } + Running = true; Task.Run(async () => { try { - _UDPServer = new UdpClient(Port, AddressFamily.InterNetwork); - // int localPort = (_UDPServer!.Client.LocalEndPoint as IPEndPoint)!.Port; - Running = true; while (!cancelToken.IsCancellationRequested) { // Read packets and trigger callbacks. @@ -127,7 +134,7 @@ public bool Send(RoveCommPacket packet, string ip, int port) } catch (Exception e) { - _logger?.LogError(e, "Failed to send UDP packet:"); + _logger?.LogError("Failed to send UDP packet: {Error}", e.Message); return false; } @@ -164,7 +171,7 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po } catch (Exception e) { - _logger?.LogError(e, "Failed to send UDP packet:"); + _logger?.LogError("Failed to send UDP packet: {Error}", e.Message); return false; } @@ -220,7 +227,7 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken) // Network problems: catch (Exception e) { - _logger?.LogError(e, "Failed to receive UDP data:"); + _logger?.LogError("Failed to receive UDP data: {Error}", e.Message); } } From c2c99af5d3f3c2693b595d46575daf99b1880b81 Mon Sep 17 00:00:00 2001 From: mjjm4d Date: Mon, 23 Sep 2024 00:18:46 -0500 Subject: [PATCH 11/15] EditorConfig and manual code cleanup --- Basestation_Software.Api/Entities/GPSWaypointRepository.cs | 2 +- Basestation_Software.Web/Program.cs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Basestation_Software.Api/Entities/GPSWaypointRepository.cs b/Basestation_Software.Api/Entities/GPSWaypointRepository.cs index 7043d86..713a44f 100644 --- a/Basestation_Software.Api/Entities/GPSWaypointRepository.cs +++ b/Basestation_Software.Api/Entities/GPSWaypointRepository.cs @@ -28,7 +28,7 @@ public GPSWaypointRepository(REDDatabase db) // Make sure the ID is null. waypoint.ID = null; // Add new row to database table. - var result = await _REDDatabase.Waypoints.AddAsync(waypoint); + Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry result = await _REDDatabase.Waypoints.AddAsync(waypoint); await _REDDatabase.SaveChangesAsync(); // Return the inserted value. return result.Entity; diff --git a/Basestation_Software.Web/Program.cs b/Basestation_Software.Web/Program.cs index 8bc2503..4e8c52f 100644 --- a/Basestation_Software.Web/Program.cs +++ b/Basestation_Software.Web/Program.cs @@ -4,6 +4,7 @@ using Radzen; var builder = WebApplication.CreateBuilder(args); +#pragma warning restore IDE0211 // Convert to 'Program.Main' style program // Add services to the container. builder.Services.AddRazorComponents() From f8ac316404f5f86edd063fa9f161109cad4aeae8 Mon Sep 17 00:00:00 2001 From: mjjm4d Date: Mon, 23 Sep 2024 00:22:51 -0500 Subject: [PATCH 12/15] Manual code cleanup --- .editorconfig | 59 ++- .../Controllers/GPSWaypointController.cs | 172 +++++---- .../Controllers/MapTileController.cs | 164 ++++---- .../Entities/GPSWaypointRepository.cs | 169 ++++----- .../Entities/IGPSWaypointRepository.cs | 21 +- .../Entities/IMapTileRepository.cs | 19 +- .../Entities/MapTileRepository.cs | 117 +++--- .../Entities/REDDatabase.cs | 173 +++++---- .../20240705001141_InitialDBMigration.cs | 81 ++-- .../Migrations/20240706231340_Maptiles.cs | 104 +++-- .../Geospatial/GPSWaypoint.cs | 25 +- .../Geospatial/GPSWaypointInput.cs | 28 +- .../Geospatial/MapTile.cs | 20 +- .../Geospatial/WaypointType.cs | 17 +- Basestation_Software.Models/Timers/ITimer.cs | 16 +- .../Timers/TaskTimer.cs | 359 +++++++++--------- .../Timers/TimerType.cs | 15 +- .../Core/Services/CookieService.cs | 132 +++---- .../Core/Services/GPSWaypointService.cs | 201 +++++----- .../Core/Services/MapTileService.cs | 147 ++++--- .../Core/Services/TaskTimerService.cs | 239 ++++++------ Basestation_Software.Web/Program.cs | 6 +- 22 files changed, 1109 insertions(+), 1175 deletions(-) diff --git a/.editorconfig b/.editorconfig index 633583f..414ce27 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,7 +8,7 @@ root = true # Indentation and spacing indent_size = 4 -indent_style = tab +indent_style = space tab_width = 4 # New line preferences @@ -56,13 +56,13 @@ dotnet_style_prefer_conditional_expression_over_assignment = true dotnet_style_prefer_conditional_expression_over_return = true dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed dotnet_style_prefer_inferred_anonymous_type_member_names = true -dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_inferred_tuple_names = true:silent dotnet_style_prefer_is_null_check_over_reference_equality_method = false dotnet_style_prefer_simplified_boolean_expressions = true dotnet_style_prefer_simplified_interpolation = true # Field preferences -dotnet_style_readonly_field = true +dotnet_style_readonly_field = false:silent # Parameter preferences dotnet_code_quality_unused_parameters = all @@ -72,7 +72,7 @@ dotnet_remove_unnecessary_suppression_exclusions = none # New line preferences dotnet_style_allow_multiple_blank_lines_experimental = false -dotnet_style_allow_statement_immediately_after_block_experimental = false:suggestion +dotnet_style_allow_statement_immediately_after_block_experimental = true #### C# Coding Conventions #### @@ -82,14 +82,14 @@ csharp_style_var_for_built_in_types = false:suggestion csharp_style_var_when_type_is_apparent = false # Expression-bodied members -csharp_style_expression_bodied_accessors = true:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_lambdas = true:silent -csharp_style_expression_bodied_local_functions = false:silent -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check = true:silent @@ -97,24 +97,24 @@ csharp_style_pattern_matching_over_is_with_cast_check = true csharp_style_prefer_extended_property_pattern = true csharp_style_prefer_not_pattern = false:silent csharp_style_prefer_pattern_matching = false -csharp_style_prefer_switch_expression = true +csharp_style_prefer_switch_expression = true:silent # Null-checking preferences csharp_style_conditional_delegate_call = true # Modifier preferences -csharp_prefer_static_anonymous_function = true -csharp_prefer_static_local_function = true +csharp_prefer_static_anonymous_function = false:silent +csharp_prefer_static_local_function = true:silent csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async -csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct = false:silent csharp_style_prefer_readonly_struct_member = true # Code-block preferences csharp_prefer_braces = when_multiline:suggestion -csharp_prefer_simple_using_statement = true:suggestion -csharp_style_namespace_declarations = block_scoped:silent -csharp_style_prefer_method_group_conversion = true:silent -csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_simple_using_statement = false +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = false csharp_style_prefer_top_level_statements = false:warning # Expression-level preferences @@ -201,14 +201,14 @@ dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case -dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members -dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case - dotnet_naming_rule.private_or_internal_field_should_be_camel_case__begins_with__.severity = suggestion dotnet_naming_rule.private_or_internal_field_should_be_camel_case__begins_with__.symbols = private_or_internal_field dotnet_naming_rule.private_or_internal_field_should_be_camel_case__begins_with__.style = camel_case__begins_with__ +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface @@ -243,14 +243,3 @@ dotnet_naming_style.camel_case__begins_with__.required_prefix = _ dotnet_naming_style.camel_case__begins_with__.required_suffix = dotnet_naming_style.camel_case__begins_with__.word_separator = dotnet_naming_style.camel_case__begins_with__.capitalization = camel_case - -[*.{cs,vb}] -dotnet_style_operator_placement_when_wrapping = beginning_of_line -tab_width = 4 -indent_size = 4 -end_of_line = crlf -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = false:suggestion -dotnet_style_prefer_auto_properties = true:silent -dotnet_style_object_initializer = true:suggestion \ No newline at end of file diff --git a/Basestation_Software.Api/Controllers/GPSWaypointController.cs b/Basestation_Software.Api/Controllers/GPSWaypointController.cs index 0b9779d..84e35f6 100644 --- a/Basestation_Software.Api/Controllers/GPSWaypointController.cs +++ b/Basestation_Software.Api/Controllers/GPSWaypointController.cs @@ -1,111 +1,109 @@ using Basestation_Software.Api.Entities; using Basestation_Software.Models.Geospatial; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace Basestation_Software.Api.Controllers +namespace Basestation_Software.Api.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class GPSWaypointController : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public class GPSWaypointController : ControllerBase + // Declare member variables. + private readonly IGPSWaypointRepository _GPSWaypointRepository; + + /// + /// Constructor + /// + /// Implicitly passed in. + public GPSWaypointController(IGPSWaypointRepository GPSWaypointRepository) { - // Declare member variables. - private readonly IGPSWaypointRepository _GPSWaypointRepository; + _GPSWaypointRepository = GPSWaypointRepository; + } - /// - /// Constructor - /// - /// Implicitly passed in. - public GPSWaypointController(IGPSWaypointRepository GPSWaypointRepository) + /// + /// IN-Code API Endpoint for adding a waypoint to the DB. + /// + /// The waypoint object. + /// The API response object. + [HttpPut] + public async Task AddGPSWaypoint(GPSWaypoint waypoint) + { + GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.AddGPSWaypoint(waypoint); + if (dbWaypoint is not null) { - _GPSWaypointRepository = GPSWaypointRepository; + return Ok(); } - - /// - /// IN-Code API Endpoint for adding a waypoint to the DB. - /// - /// The waypoint object. - /// The API response object. - [HttpPut] - public async Task AddGPSWaypoint(GPSWaypoint waypoint) + else { - GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.AddGPSWaypoint(waypoint); - if (dbWaypoint is not null) - { - return Ok(); - } - else - { - return BadRequest(); - } + return BadRequest(); } + } - /// - /// IN-Code API Endpoint for removing a waypoint from the DB. - /// - /// The waypoint ID. - /// The API response object. - [HttpDelete("{waypointID}")] - public async Task DeleteGPSWaypoint(int waypointID) + /// + /// IN-Code API Endpoint for removing a waypoint from the DB. + /// + /// The waypoint ID. + /// The API response object. + [HttpDelete("{waypointID}")] + public async Task DeleteGPSWaypoint(int waypointID) + { + GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.DeleteGPSWaypoint(waypointID); + if (dbWaypoint is not null) { - GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.DeleteGPSWaypoint(waypointID); - if (dbWaypoint is not null) - { - return Ok(); - } - else - { - return NotFound(); - } + return Ok(); } - - /// - /// IN-Code API Endpoint for getting a waypoint to the DB. - /// - /// The waypoint id. - /// The API response object. - [HttpGet("{waypointID}")] - public async Task GetGPSWaypoint(int waypointID) + else { - GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.GetGPSWaypoint(waypointID); - if (dbWaypoint is not null) - { - return Ok(dbWaypoint); - } - else - { - return NotFound(); - } + return NotFound(); } + } - /// - /// IN-Code API Endpoint for getting all waypoints from the DB. - /// - /// The API response object. - [HttpGet] - public async Task GetAllGPSWaypoints() + /// + /// IN-Code API Endpoint for getting a waypoint to the DB. + /// + /// The waypoint id. + /// The API response object. + [HttpGet("{waypointID}")] + public async Task GetGPSWaypoint(int waypointID) + { + GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.GetGPSWaypoint(waypointID); + if (dbWaypoint is not null) { - return Ok(await _GPSWaypointRepository.GetAllGPSWaypoints()); + return Ok(dbWaypoint); } - - /// - /// IN-Code API Endpoint for updating a waypoint to the DB. - /// - /// The waypoint object. - /// The API response object. - [HttpPost] - public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) + else { - GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.UpdateGPSWaypoint(waypoint); - if (dbWaypoint is not null) - { - return Ok(dbWaypoint); - } - else - { - return NotFound(); - } + return NotFound(); } + } + /// + /// IN-Code API Endpoint for getting all waypoints from the DB. + /// + /// The API response object. + [HttpGet] + public async Task GetAllGPSWaypoints() + { + return Ok(await _GPSWaypointRepository.GetAllGPSWaypoints()); + } + + /// + /// IN-Code API Endpoint for updating a waypoint to the DB. + /// + /// The waypoint object. + /// The API response object. + [HttpPost] + public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) + { + GPSWaypoint? dbWaypoint = await _GPSWaypointRepository.UpdateGPSWaypoint(waypoint); + if (dbWaypoint is not null) + { + return Ok(dbWaypoint); + } + else + { + return NotFound(); + } } + } diff --git a/Basestation_Software.Api/Controllers/MapTileController.cs b/Basestation_Software.Api/Controllers/MapTileController.cs index f85ef81..dfbb1f7 100644 --- a/Basestation_Software.Api/Controllers/MapTileController.cs +++ b/Basestation_Software.Api/Controllers/MapTileController.cs @@ -1,111 +1,109 @@ using Basestation_Software.Api.Entities; using Basestation_Software.Models.Geospatial; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; -namespace Basestation_Software.Api.Controllers +namespace Basestation_Software.Api.Controllers; + +[Route("api/[controller]")] +[ApiController] +public class MapTilesController : ControllerBase { - [Route("api/[controller]")] - [ApiController] - public class MapTilesController : ControllerBase + // Declare member variables. + private readonly IMapTileRepository _TileRepository; + + /// + /// Constructor + /// + /// + public MapTilesController(IMapTileRepository tileRepository) { - // Declare member variables. - private readonly IMapTileRepository _TileRepository; + _TileRepository = tileRepository; + } - /// - /// Constructor - /// - /// - public MapTilesController(IMapTileRepository tileRepository) + [HttpPut] + public async Task AddMapTile(MapTile tile) + { + MapTile? dbTile = await _TileRepository.AddMapTile(tile); + if (dbTile is not null) { - _TileRepository = tileRepository; + return Ok(); } - - [HttpPut] - public async Task AddMapTile(MapTile tile) + else { - MapTile? dbTile = await _TileRepository.AddMapTile(tile); - if (dbTile is not null) - { - return Ok(); - } - else - { - return BadRequest(); - } + return BadRequest(); } + } - [HttpPost] - public async Task UpdateMapTile(MapTile tile) + [HttpPost] + public async Task UpdateMapTile(MapTile tile) + { + MapTile? dbTile = await _TileRepository.UpdateMapTile(tile); + if (dbTile is not null) { - MapTile? dbTile = await _TileRepository.UpdateMapTile(tile); - if (dbTile is not null) - { - return Ok(dbTile); - } - else - { - return NotFound(); - } + return Ok(dbTile); } - - [HttpDelete("{tileID}")] - public async Task DeleteMapTile(int tileID) + else { - MapTile? dbTile = await _TileRepository.DeleteMapTile(tileID); - if (dbTile is not null) - { - return Ok(); - } - else - { - return NotFound(); - } + return NotFound(); } + } - [HttpDelete("{z}/{y}/{x}")] - public async Task DeleteMapTile(int z, int y, int x) + [HttpDelete("{tileID}")] + public async Task DeleteMapTile(int tileID) + { + MapTile? dbTile = await _TileRepository.DeleteMapTile(tileID); + if (dbTile is not null) { - MapTile? tileToDelete = await _TileRepository.GetMapTile(x, y, z); - MapTile? dbTile = await _TileRepository.DeleteMapTile(tileToDelete?.ID ?? -1); - if (dbTile is not null) - { - return Ok(); - } - else - { - return NotFound(); - } + return Ok(); } - - [HttpGet("{z}/{y}/{x}.png")] - public async Task GetMapTileImage(int z, int y, int x) + else { - MapTile? dbTile = await _TileRepository.GetMapTile(x, y, z); - if (dbTile is not null && dbTile.ImageData is not null) - { - return File(dbTile.ImageData, "image/png"); - } - else - { - return NotFound(); - } + return NotFound(); + } + } + [HttpDelete("{z}/{y}/{x}")] + public async Task DeleteMapTile(int z, int y, int x) + { + MapTile? tileToDelete = await _TileRepository.GetMapTile(x, y, z); + MapTile? dbTile = await _TileRepository.DeleteMapTile(tileToDelete?.ID ?? -1); + if (dbTile is not null) + { + return Ok(); } + else + { + return NotFound(); + } + } - [HttpGet("{z}/{y}/{x}")] - public async Task GetMapTile(int z, int y, int x) + [HttpGet("{z}/{y}/{x}.png")] + public async Task GetMapTileImage(int z, int y, int x) + { + MapTile? dbTile = await _TileRepository.GetMapTile(x, y, z); + if (dbTile is not null && dbTile.ImageData is not null) { - MapTile? dbTile = await _TileRepository.GetMapTile(x, y, z); - if (dbTile is not null) - { - return Ok(dbTile); - } - else - { - return NotFound(); - } + return File(dbTile.ImageData, "image/png"); + } + else + { + return NotFound(); + } + } + + [HttpGet("{z}/{y}/{x}")] + public async Task GetMapTile(int z, int y, int x) + { + MapTile? dbTile = await _TileRepository.GetMapTile(x, y, z); + if (dbTile is not null) + { + return Ok(dbTile); } + else + { + return NotFound(); + } + } } \ No newline at end of file diff --git a/Basestation_Software.Api/Entities/GPSWaypointRepository.cs b/Basestation_Software.Api/Entities/GPSWaypointRepository.cs index 713a44f..1931cb0 100644 --- a/Basestation_Software.Api/Entities/GPSWaypointRepository.cs +++ b/Basestation_Software.Api/Entities/GPSWaypointRepository.cs @@ -1,109 +1,108 @@ -using System.Reflection; using Basestation_Software.Models.Geospatial; using Microsoft.EntityFrameworkCore; +using System.Reflection; -namespace Basestation_Software.Api.Entities +namespace Basestation_Software.Api.Entities; + +public class GPSWaypointRepository : IGPSWaypointRepository { - public class GPSWaypointRepository : IGPSWaypointRepository + // Declare member variables. + private readonly REDDatabase _REDDatabase; + + /// + /// Constructor + /// + /// Implicitly passed in. + public GPSWaypointRepository(REDDatabase db) { - // Declare member variables. - private readonly REDDatabase _REDDatabase; + _REDDatabase = db; + } - /// - /// Constructor - /// - /// Implicitly passed in. - public GPSWaypointRepository(REDDatabase db) - { - _REDDatabase = db; - } + /// + /// Add a GPS waypoint to the database. + /// + /// The new GPS waypoint. + /// The object stored in the DB. + public async Task AddGPSWaypoint(GPSWaypoint waypoint) + { + // Make sure the ID is null. + waypoint.ID = null; + // Add new row to database table. + var result = await _REDDatabase.Waypoints.AddAsync(waypoint); + await _REDDatabase.SaveChangesAsync(); + // Return the inserted value. + return result.Entity; + } - /// - /// Add a GPS waypoint to the database. - /// - /// The new GPS waypoint. - /// The object stored in the DB. - public async Task AddGPSWaypoint(GPSWaypoint waypoint) + /// + /// Remove a GPS waypoint from the database. + /// + /// The ID of the waypoint to remove. + public async Task DeleteGPSWaypoint(int waypointID) + { + // Find the first waypoint with the same ID. + GPSWaypoint? result = await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypointID); + // Check if it was found. + if (result is not null) { - // Make sure the ID is null. - waypoint.ID = null; - // Add new row to database table. - Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry result = await _REDDatabase.Waypoints.AddAsync(waypoint); + // Remove the row from the database. + _REDDatabase.Waypoints.Remove(result); await _REDDatabase.SaveChangesAsync(); - // Return the inserted value. - return result.Entity; } + return result; + } - /// - /// Remove a GPS waypoint from the database. - /// - /// The ID of the waypoint to remove. - public async Task DeleteGPSWaypoint(int waypointID) - { - // Find the first waypoint with the same ID. - GPSWaypoint? result = await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypointID); - // Check if it was found. - if (result is not null) - { - // Remove the row from the database. - _REDDatabase.Waypoints.Remove(result); - await _REDDatabase.SaveChangesAsync(); - } - return result; - } + /// + /// Get all GPS waypoints in the DB. + /// + /// A list of GPSWaypoint objects. + public async Task> GetAllGPSWaypoints() + { + return await _REDDatabase.Waypoints.ToListAsync(); + } - /// - /// Get all GPS waypoints in the DB. - /// - /// A list of GPSWaypoint objects. - public async Task> GetAllGPSWaypoints() - { - return await _REDDatabase.Waypoints.ToListAsync(); - } + /// + /// Get a waypoint from the DB. + /// + /// The ID of the waypoint to return. + /// A GPSWaypoint object, null if not found. + public async Task GetGPSWaypoint(int waypointID) + { + return await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypointID); + } - /// - /// Get a waypoint from the DB. - /// - /// The ID of the waypoint to return. - /// A GPSWaypoint object, null if not found. - public async Task GetGPSWaypoint(int waypointID) + /// + /// Update the data for a GPSWaypoint in the DB. + /// + /// A GPSWaypoint object containing the new data. + /// The object stored in the database. + public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) + { + // Find the waypoint object to update in the DB. + GPSWaypoint? result = await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypoint.ID); + // Check if we found it. + if (result is not null) { - return await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypointID); - } + // Get the type of the GPSWaypoint class + Type type = typeof(GPSWaypoint); - /// - /// Update the data for a GPSWaypoint in the DB. - /// - /// A GPSWaypoint object containing the new data. - /// The object stored in the database. - public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) - { - // Find the waypoint object to update in the DB. - GPSWaypoint? result = await _REDDatabase.Waypoints.FirstOrDefaultAsync(x => x.ID == waypoint.ID); - // Check if we found it. - if (result is not null) + // Iterate over all properties of the GPSWaypoint class + foreach (PropertyInfo property in type.GetProperties()) { - // Get the type of the GPSWaypoint class - Type type = typeof(GPSWaypoint); + // Get the value of the property from the incoming waypoint object + object? newValue = property.GetValue(waypoint); - // Iterate over all properties of the GPSWaypoint class - foreach (PropertyInfo property in type.GetProperties()) + // If the new value is not null, update the property in the result object + if (newValue != null) { - // Get the value of the property from the incoming waypoint object - object? newValue = property.GetValue(waypoint); - - // If the new value is not null, update the property in the result object - if (newValue != null) - { - property.SetValue(result, newValue); - } + property.SetValue(result, newValue); } - - // Save changes to DB. - await _REDDatabase.SaveChangesAsync(); } - return result; + // Save changes to DB. + await _REDDatabase.SaveChangesAsync(); } + + return result; } } \ No newline at end of file diff --git a/Basestation_Software.Api/Entities/IGPSWaypointRepository.cs b/Basestation_Software.Api/Entities/IGPSWaypointRepository.cs index f5ec80d..191b2d3 100644 --- a/Basestation_Software.Api/Entities/IGPSWaypointRepository.cs +++ b/Basestation_Software.Api/Entities/IGPSWaypointRepository.cs @@ -1,17 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Api.Entities +namespace Basestation_Software.Api.Entities; + +public interface IGPSWaypointRepository { - public interface IGPSWaypointRepository - { - Task AddGPSWaypoint(GPSWaypoint waypoint); - Task DeleteGPSWaypoint(int waypointID); - Task> GetAllGPSWaypoints(); - Task GetGPSWaypoint(int waypointID); - Task UpdateGPSWaypoint(GPSWaypoint waypoint); - } + Task AddGPSWaypoint(GPSWaypoint waypoint); + Task DeleteGPSWaypoint(int waypointID); + Task> GetAllGPSWaypoints(); + Task GetGPSWaypoint(int waypointID); + Task UpdateGPSWaypoint(GPSWaypoint waypoint); } \ No newline at end of file diff --git a/Basestation_Software.Api/Entities/IMapTileRepository.cs b/Basestation_Software.Api/Entities/IMapTileRepository.cs index a537ec1..d667e9c 100644 --- a/Basestation_Software.Api/Entities/IMapTileRepository.cs +++ b/Basestation_Software.Api/Entities/IMapTileRepository.cs @@ -1,16 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Api.Entities +namespace Basestation_Software.Api.Entities; + +public interface IMapTileRepository { - public interface IMapTileRepository - { - Task AddMapTile(MapTile tile); - Task UpdateMapTile(MapTile tile); - Task GetMapTile(int x, int y, int z); - Task DeleteMapTile(int tileID); - } + Task AddMapTile(MapTile tile); + Task UpdateMapTile(MapTile tile); + Task GetMapTile(int x, int y, int z); + Task DeleteMapTile(int tileID); } \ No newline at end of file diff --git a/Basestation_Software.Api/Entities/MapTileRepository.cs b/Basestation_Software.Api/Entities/MapTileRepository.cs index f099a71..e8ed511 100644 --- a/Basestation_Software.Api/Entities/MapTileRepository.cs +++ b/Basestation_Software.Api/Entities/MapTileRepository.cs @@ -1,81 +1,80 @@ -using System.Reflection; using Basestation_Software.Models.Geospatial; using Microsoft.EntityFrameworkCore; +using System.Reflection; -namespace Basestation_Software.Api.Entities +namespace Basestation_Software.Api.Entities; + +public class MapTileRepository : IMapTileRepository { - public class MapTileRepository : IMapTileRepository + // Declare member variables. + private readonly REDDatabase _REDDatabase; + + /// + /// Constructor + /// + /// + public MapTileRepository(REDDatabase db) { - // Declare member variables. - private readonly REDDatabase _REDDatabase; + _REDDatabase = db; + } - /// - /// Constructor - /// - /// - public MapTileRepository(REDDatabase db) - { - _REDDatabase = db; - } + public async Task AddMapTile(MapTile tile) + { + // Make sure the ID is null. + tile.ID = null; + // Add a new tile to the database. + var result = await _REDDatabase.MapTiles.AddAsync(tile); + await _REDDatabase.SaveChangesAsync(); + // Return the inserted value. + return result.Entity; + } - public async Task AddMapTile(MapTile tile) + public async Task UpdateMapTile(MapTile tile) + { + // Find the tile object to update in the DB. + MapTile? result = await _REDDatabase.MapTiles.FirstOrDefaultAsync(x => x.ID == tile.ID); + // Check if we found it. + if (result is not null) { - // Make sure the ID is null. - tile.ID = null; - // Add a new tile to the database. - var result = await _REDDatabase.MapTiles.AddAsync(tile); - await _REDDatabase.SaveChangesAsync(); - // Return the inserted value. - return result.Entity; - } + // Get the type of the GPStile class + Type type = typeof(MapTile); - public async Task UpdateMapTile(MapTile tile) - { - // Find the tile object to update in the DB. - MapTile? result = await _REDDatabase.MapTiles.FirstOrDefaultAsync(x => x.ID == tile.ID); - // Check if we found it. - if (result is not null) + // Iterate over all properties of the GPStile class + foreach (PropertyInfo property in type.GetProperties()) { - // Get the type of the GPStile class - Type type = typeof(MapTile); + // Get the value of the property from the incoming tile object + object? newValue = property.GetValue(tile); - // Iterate over all properties of the GPStile class - foreach (PropertyInfo property in type.GetProperties()) + // If the new value is not null, update the property in the result object + if (newValue != null) { - // Get the value of the property from the incoming tile object - object? newValue = property.GetValue(tile); - - // If the new value is not null, update the property in the result object - if (newValue != null) - { - property.SetValue(result, newValue); - } + property.SetValue(result, newValue); } - - // Save changes to DB. - await _REDDatabase.SaveChangesAsync(); } - return result; + // Save changes to DB. + await _REDDatabase.SaveChangesAsync(); } - public async Task DeleteMapTile(int tileID) - { - // Find the first tile with the same ID. - MapTile? result = await _REDDatabase.MapTiles.FirstOrDefaultAsync(x => x.ID == tileID); - // Check if it was found. - if (result is not null) - { - // Remove from the db. - _REDDatabase.MapTiles.Remove(result); - await _REDDatabase.SaveChangesAsync(); - } - return result; - } + return result; + } - public async Task GetMapTile(int x, int y, int z) + public async Task DeleteMapTile(int tileID) + { + // Find the first tile with the same ID. + MapTile? result = await _REDDatabase.MapTiles.FirstOrDefaultAsync(x => x.ID == tileID); + // Check if it was found. + if (result is not null) { - return await _REDDatabase.MapTiles.FirstOrDefaultAsync(tile => tile.X == x && tile.Y == y && tile.Z == z); + // Remove from the db. + _REDDatabase.MapTiles.Remove(result); + await _REDDatabase.SaveChangesAsync(); } + return result; + } + + public async Task GetMapTile(int x, int y, int z) + { + return await _REDDatabase.MapTiles.FirstOrDefaultAsync(tile => tile.X == x && tile.Y == y && tile.Z == z); } } \ No newline at end of file diff --git a/Basestation_Software.Api/Entities/REDDatabase.cs b/Basestation_Software.Api/Entities/REDDatabase.cs index 09fd5ee..84b5392 100644 --- a/Basestation_Software.Api/Entities/REDDatabase.cs +++ b/Basestation_Software.Api/Entities/REDDatabase.cs @@ -2,99 +2,98 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; -namespace Basestation_Software.Api.Entities +namespace Basestation_Software.Api.Entities; + +public class REDDatabase : DbContext { - public class REDDatabase : DbContext - { - private readonly IConfiguration Configuration; + private readonly IConfiguration Configuration; - /// - /// Constructor - /// - /// The configuration that contains the DB connection string and params. (Implicitly passed in) - public REDDatabase(IConfiguration configuration) - { - // Assign member variables. - Configuration = configuration; - } + /// + /// Constructor + /// + /// The configuration that contains the DB connection string and params. (Implicitly passed in) + public REDDatabase(IConfiguration configuration) + { + // Assign member variables. + Configuration = configuration; + } - /// - /// This is used by the Entity Framework Core to configure the database context. - /// - /// - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - optionsBuilder.UseSqlite(Configuration.GetConnectionString("RED_DB")); - } + /// + /// This is used by the Entity Framework Core to configure the database context. + /// + /// + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlite(Configuration.GetConnectionString("RED_DB")); + } - /// - /// This is used by the Entity Framework Core and represents a collection of - /// entities in a context. In this case is corresponds to a database table. - /// - /// - public DbSet Waypoints { get; set; } - public DbSet MapTiles { get; set; } + /// + /// This is used by the Entity Framework Core and represents a collection of + /// entities in a context. In this case is corresponds to a database table. + /// + /// + public DbSet Waypoints { get; set; } + public DbSet MapTiles { get; set; } - /// - /// Configure the primary key for the Waypoints table. - /// - /// - public void Configure(EntityTypeBuilder modelBuilder) - { - modelBuilder.HasKey(x => x.ID); - modelBuilder.Property(x => x.ID) - .HasColumnName(@"ID") - //.HasColumnType("int") Weirdly this was upsetting SQLite - .IsRequired() - .ValueGeneratedOnAdd() - ; - } - public void Configure(EntityTypeBuilder modelBuilder) - { - modelBuilder.HasKey(x => x.ID); - modelBuilder.Property(x => x.ID) - .HasColumnName(@"ID") - //.HasColumnType("int") Weirdly this was upsetting SQLite - .IsRequired() - .ValueGeneratedOnAdd() - ; - } + /// + /// Configure the primary key for the Waypoints table. + /// + /// + public void Configure(EntityTypeBuilder modelBuilder) + { + modelBuilder.HasKey(x => x.ID); + modelBuilder.Property(x => x.ID) + .HasColumnName(@"ID") + //.HasColumnType("int") Weirdly this was upsetting SQLite + .IsRequired() + .ValueGeneratedOnAdd() + ; + } + public void Configure(EntityTypeBuilder modelBuilder) + { + modelBuilder.HasKey(x => x.ID); + modelBuilder.Property(x => x.ID) + .HasColumnName(@"ID") + //.HasColumnType("int") Weirdly this was upsetting SQLite + .IsRequired() + .ValueGeneratedOnAdd() + ; + } - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); - /* - Add some default GPS waypoints to the Waypoint database table. - */ - // SDELC - modelBuilder.Entity().HasData( - new GPSWaypoint - { - ID = 1, - Name = "MDRS", - Latitude = 38.405879, - Longitude = -110.792207, - Altitude = 1280.0, - WaypointColor = System.Drawing.Color.Green.ToArgb(), - SearchRadius = 5.0, - Type = WaypointType.Navigation - } - ); - // MDRS - modelBuilder.Entity().HasData( - new GPSWaypoint - { - ID = 2, - Name = "SDELC", - Latitude = 37.951764, - Longitude = -91.778441, - Altitude = 315.0, - WaypointColor = System.Drawing.Color.Red.ToArgb(), - SearchRadius = 5.0, - Type = WaypointType.Navigation - } - ); - } + /* + Add some default GPS waypoints to the Waypoint database table. + */ + // SDELC + modelBuilder.Entity().HasData( + new GPSWaypoint + { + ID = 1, + Name = "MDRS", + Latitude = 38.405879, + Longitude = -110.792207, + Altitude = 1280.0, + WaypointColor = System.Drawing.Color.Green.ToArgb(), + SearchRadius = 5.0, + Type = WaypointType.Navigation + } + ); + // MDRS + modelBuilder.Entity().HasData( + new GPSWaypoint + { + ID = 2, + Name = "SDELC", + Latitude = 37.951764, + Longitude = -91.778441, + Altitude = 315.0, + WaypointColor = System.Drawing.Color.Red.ToArgb(), + SearchRadius = 5.0, + Type = WaypointType.Navigation + } + ); } } \ No newline at end of file diff --git a/Basestation_Software.Api/Migrations/20240705001141_InitialDBMigration.cs b/Basestation_Software.Api/Migrations/20240705001141_InitialDBMigration.cs index d86439e..d311811 100644 --- a/Basestation_Software.Api/Migrations/20240705001141_InitialDBMigration.cs +++ b/Basestation_Software.Api/Migrations/20240705001141_InitialDBMigration.cs @@ -1,53 +1,50 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -#pragma warning disable CA1814 // Prefer jagged arrays over multidimensional -namespace Basestation_Software.Api.Migrations +namespace Basestation_Software.Api.Migrations; + +/// +public partial class InitialDBMigration : Migration { /// - public partial class InitialDBMigration : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Waypoints", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(type: "TEXT", nullable: true), - Latitude = table.Column(type: "REAL", nullable: true), - Longitude = table.Column(type: "REAL", nullable: true), - Altitude = table.Column(type: "REAL", nullable: true), - Timestamp = table.Column(type: "TEXT", nullable: true), - WaypointColor = table.Column(type: "INTEGER", nullable: true), - SearchRadius = table.Column(type: "REAL", nullable: true), - Type = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Waypoints", x => x.ID); - }); + migrationBuilder.CreateTable( + name: "Waypoints", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Name = table.Column(type: "TEXT", nullable: true), + Latitude = table.Column(type: "REAL", nullable: true), + Longitude = table.Column(type: "REAL", nullable: true), + Altitude = table.Column(type: "REAL", nullable: true), + Timestamp = table.Column(type: "TEXT", nullable: true), + WaypointColor = table.Column(type: "INTEGER", nullable: true), + SearchRadius = table.Column(type: "REAL", nullable: true), + Type = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Waypoints", x => x.ID); + }); - migrationBuilder.InsertData( - table: "Waypoints", - columns: new[] { "ID", "Altitude", "Latitude", "Longitude", "Name", "SearchRadius", "Timestamp", "Type", "WaypointColor" }, - values: new object[,] - { - { 1, 1280.0, 38.405878999999999, -110.792207, "MDRS", 5.0, new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8159), 0, -16744448 }, - { 2, 315.0, 37.951763999999997, -91.778441000000001, "SDELC", 5.0, new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8289), 0, -65536 } - }); - } + migrationBuilder.InsertData( + table: "Waypoints", + columns: new[] { "ID", "Altitude", "Latitude", "Longitude", "Name", "SearchRadius", "Timestamp", "Type", "WaypointColor" }, + values: new object[,] + { + { 1, 1280.0, 38.405878999999999, -110.792207, "MDRS", 5.0, new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8159), 0, -16744448 }, + { 2, 315.0, 37.951763999999997, -91.778441000000001, "SDELC", 5.0, new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8289), 0, -65536 } + }); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Waypoints"); - } + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Waypoints"); } } diff --git a/Basestation_Software.Api/Migrations/20240706231340_Maptiles.cs b/Basestation_Software.Api/Migrations/20240706231340_Maptiles.cs index 84514fa..0c91e5b 100644 --- a/Basestation_Software.Api/Migrations/20240706231340_Maptiles.cs +++ b/Basestation_Software.Api/Migrations/20240706231340_Maptiles.cs @@ -1,66 +1,64 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace Basestation_Software.Api.Migrations +namespace Basestation_Software.Api.Migrations; + +/// +public partial class Maptiles : Migration { /// - public partial class Maptiles : Migration + protected override void Up(MigrationBuilder migrationBuilder) { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "MapTiles", - columns: table => new - { - ID = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - X = table.Column(type: "INTEGER", nullable: true), - Y = table.Column(type: "INTEGER", nullable: true), - Z = table.Column(type: "INTEGER", nullable: true), - ImageData = table.Column(type: "BLOB", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_MapTiles", x => x.ID); - }); + migrationBuilder.CreateTable( + name: "MapTiles", + columns: table => new + { + ID = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + X = table.Column(type: "INTEGER", nullable: true), + Y = table.Column(type: "INTEGER", nullable: true), + Z = table.Column(type: "INTEGER", nullable: true), + ImageData = table.Column(type: "BLOB", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_MapTiles", x => x.ID); + }); - migrationBuilder.UpdateData( - table: "Waypoints", - keyColumn: "ID", - keyValue: 1, - column: "Timestamp", - value: new DateTime(2024, 7, 6, 18, 13, 39, 408, DateTimeKind.Local).AddTicks(2836)); + migrationBuilder.UpdateData( + table: "Waypoints", + keyColumn: "ID", + keyValue: 1, + column: "Timestamp", + value: new DateTime(2024, 7, 6, 18, 13, 39, 408, DateTimeKind.Local).AddTicks(2836)); - migrationBuilder.UpdateData( - table: "Waypoints", - keyColumn: "ID", - keyValue: 2, - column: "Timestamp", - value: new DateTime(2024, 7, 6, 18, 13, 39, 408, DateTimeKind.Local).AddTicks(2958)); - } + migrationBuilder.UpdateData( + table: "Waypoints", + keyColumn: "ID", + keyValue: 2, + column: "Timestamp", + value: new DateTime(2024, 7, 6, 18, 13, 39, 408, DateTimeKind.Local).AddTicks(2958)); + } - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "MapTiles"); + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "MapTiles"); - migrationBuilder.UpdateData( - table: "Waypoints", - keyColumn: "ID", - keyValue: 1, - column: "Timestamp", - value: new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8159)); + migrationBuilder.UpdateData( + table: "Waypoints", + keyColumn: "ID", + keyValue: 1, + column: "Timestamp", + value: new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8159)); - migrationBuilder.UpdateData( - table: "Waypoints", - keyColumn: "ID", - keyValue: 2, - column: "Timestamp", - value: new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8289)); - } + migrationBuilder.UpdateData( + table: "Waypoints", + keyColumn: "ID", + keyValue: 2, + column: "Timestamp", + value: new DateTime(2024, 7, 4, 19, 11, 41, 140, DateTimeKind.Local).AddTicks(8289)); } } diff --git a/Basestation_Software.Models/Geospatial/GPSWaypoint.cs b/Basestation_Software.Models/Geospatial/GPSWaypoint.cs index b024cb8..79e3524 100644 --- a/Basestation_Software.Models/Geospatial/GPSWaypoint.cs +++ b/Basestation_Software.Models/Geospatial/GPSWaypoint.cs @@ -1,17 +1,14 @@ -using System.Drawing; +namespace Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Models.Geospatial +public class GPSWaypoint { - public class GPSWaypoint - { - public int? ID { get; set; } - public string? Name { get; set; } - public double? Latitude { get; set; } - public double? Longitude { get; set;} - public double? Altitude { get; set; } - public DateTime? Timestamp { get; set; } = DateTime.Now; - public int? WaypointColor { get; set; } - public double? SearchRadius { get; set; } - public WaypointType? Type { get; set; } - } + public int? ID { get; set; } + public string? Name { get; set; } + public double? Latitude { get; set; } + public double? Longitude { get; set; } + public double? Altitude { get; set; } + public DateTime? Timestamp { get; set; } = DateTime.Now; + public int? WaypointColor { get; set; } + public double? SearchRadius { get; set; } + public WaypointType? Type { get; set; } } \ No newline at end of file diff --git a/Basestation_Software.Models/Geospatial/GPSWaypointInput.cs b/Basestation_Software.Models/Geospatial/GPSWaypointInput.cs index c1dcd62..e01496f 100644 --- a/Basestation_Software.Models/Geospatial/GPSWaypointInput.cs +++ b/Basestation_Software.Models/Geospatial/GPSWaypointInput.cs @@ -1,20 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Models.Geospatial +public class GPSWaypointInput { - public class GPSWaypointInput - { - public int ID { get; set; } = -1; - public string Name { get; set; } = ""; - public double Latitude { get; set; } = 0; - public double Longitude { get; set; } = 0; - public double Altitude { get; set; } = 0; - public string Timestamp { get; set; } = DateTime.Now.ToString(); - public string WaypointColor { get; set; } = "rgb(0, 0, 0)"; - public double SearchRadius { get; set; } = 0; - public string Type { get; set; } = "Navigation"; - } + public int ID { get; set; } = -1; + public string Name { get; set; } = ""; + public double Latitude { get; set; } = 0; + public double Longitude { get; set; } = 0; + public double Altitude { get; set; } = 0; + public string Timestamp { get; set; } = DateTime.Now.ToString(); + public string WaypointColor { get; set; } = "rgb(0, 0, 0)"; + public double SearchRadius { get; set; } = 0; + public string Type { get; set; } = "Navigation"; } \ No newline at end of file diff --git a/Basestation_Software.Models/Geospatial/MapTile.cs b/Basestation_Software.Models/Geospatial/MapTile.cs index 09bb911..f43a18a 100644 --- a/Basestation_Software.Models/Geospatial/MapTile.cs +++ b/Basestation_Software.Models/Geospatial/MapTile.cs @@ -1,16 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Models.Geospatial +public class MapTile { - public class MapTile - { - public int? ID { get; set; } - public int? X { get; set; } - public int? Y { get; set; } - public int? Z { get; set; } - public byte[]? ImageData { get; set; } - } + public int? ID { get; set; } + public int? X { get; set; } + public int? Y { get; set; } + public int? Z { get; set; } + public byte[]? ImageData { get; set; } } \ No newline at end of file diff --git a/Basestation_Software.Models/Geospatial/WaypointType.cs b/Basestation_Software.Models/Geospatial/WaypointType.cs index f55d55b..e66f2c2 100644 --- a/Basestation_Software.Models/Geospatial/WaypointType.cs +++ b/Basestation_Software.Models/Geospatial/WaypointType.cs @@ -1,11 +1,10 @@ -namespace Basestation_Software.Models.Geospatial +namespace Basestation_Software.Models.Geospatial; + +public enum WaypointType { - public enum WaypointType - { - Navigation, - Marker, - Object, - Gate, - Obstacle - } + Navigation, + Marker, + Object, + Gate, + Obstacle } \ No newline at end of file diff --git a/Basestation_Software.Models/Timers/ITimer.cs b/Basestation_Software.Models/Timers/ITimer.cs index 6c1db89..d738e86 100644 --- a/Basestation_Software.Models/Timers/ITimer.cs +++ b/Basestation_Software.Models/Timers/ITimer.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Basestation_Software.Models.Timers; -namespace Basestation_Software.Models.Timers +public interface ITimer { - public interface ITimer - { - public void Start(int updateIntervalMS); - public void Stop(); - public void Reset(); - } + public void Start(int updateIntervalMS); + public void Stop(); + public void Reset(); } \ No newline at end of file diff --git a/Basestation_Software.Models/Timers/TaskTimer.cs b/Basestation_Software.Models/Timers/TaskTimer.cs index 34779bb..1d67d2e 100644 --- a/Basestation_Software.Models/Timers/TaskTimer.cs +++ b/Basestation_Software.Models/Timers/TaskTimer.cs @@ -1,234 +1,227 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +namespace Basestation_Software.Models.Timers; -namespace Basestation_Software.Models.Timers +public class TaskTimer : ITimer { - public class TaskTimer : ITimer + // Declare member variables. + public Timer? Timer { get; private set; } + public TaskType TaskType { get; private set; } = TaskType.Autonomy; + private bool _startPointChanged = false; + private DateTime _startPoint = DateTime.MinValue; + public DateTime StartPoint { - // Declare member variables. - public Timer? Timer { get; private set; } - public TaskType TaskType { get; private set; } = TaskType.Autonomy; - private bool _startPointChanged = false; - private DateTime _startPoint = DateTime.MinValue; - public DateTime StartPoint + get { return _startPoint; } + set { - get { return _startPoint; } - set - { - _startPoint = value; - _startPointChanged = true; - } + _startPoint = value; + _startPointChanged = true; } - public TimeSpan EndPoint { get; set; } = TimeSpan.MaxValue; - public TimeSpan ElapsedTime { get; private set; } = TimeSpan.Zero; - public Dictionary CheckPoints { get; set; } = new Dictionary(); - public bool IsStarted { get; private set; } = false; - public bool IsPaused { get; private set; } = false; - public bool IsFinished { get; private set; } = false; - public bool IsBeingConfigured { get; private set; } = false; - - // Delegates and events. - public delegate Task TimerTickCallback(TaskType timerTaskType, TimeSpan elapsedTime); - private event TimerTickCallback? TimerTickNotifier; - - /// - /// Constructor. - /// - /// - /// - public TaskTimer(TimerTickCallback callback, TaskType timerType) + } + public TimeSpan EndPoint { get; set; } = TimeSpan.MaxValue; + public TimeSpan ElapsedTime { get; private set; } = TimeSpan.Zero; + public Dictionary CheckPoints { get; set; } = []; + public bool IsStarted { get; private set; } = false; + public bool IsPaused { get; private set; } = false; + public bool IsFinished { get; private set; } = false; + public bool IsBeingConfigured { get; private set; } = false; + + // Delegates and events. + public delegate Task TimerTickCallback(TaskType timerTaskType, TimeSpan elapsedTime); + private event TimerTickCallback? TimerTickNotifier; + + /// + /// Constructor. + /// + /// + /// + public TaskTimer(TimerTickCallback callback, TaskType timerType) + { + // Assigm member variables. + TimerTickNotifier += callback; + TaskType = timerType; + } + + /// + /// Start the timer. + /// + /// The interval in ms that the timer metrics and callbacks will be updated/invoked. + public void Start(int updateIntervalMS = 100) + { + if (IsFinished) { - // Assigm member variables. - TimerTickNotifier += callback; - TaskType = timerType; + return; } - - /// - /// Start the timer. - /// - /// The interval in ms that the timer metrics and callbacks will be updated/invoked. - public void Start(int updateIntervalMS = 100) + if (!IsPaused) { - if (IsFinished) - { - return; - } - if (!IsPaused) - { - StartPoint = DateTime.Now; - // Create new timer. - Timer = new Timer(UpdateElapsedTime, null, 0, updateIntervalMS); - } - else - { - // Offest the start point by the elapsed time. - StartPoint = DateTime.Now - ElapsedTime; - } - IsStarted = true; - IsPaused = false; - IsFinished = false; + StartPoint = DateTime.Now; + // Create new timer. + Timer = new Timer(UpdateElapsedTime, null, 0, updateIntervalMS); } - - /// - /// Pause the timer. - /// - public void Stop() + else { - if (!IsStarted || IsFinished) - { - return; - } - IsStarted = true; - IsPaused = true; - IsFinished = false; + // Offest the start point by the elapsed time. + StartPoint = DateTime.Now - ElapsedTime; } + IsStarted = true; + IsPaused = false; + IsFinished = false; + } - /// - /// Reset the timer back to its initial state. - /// - public void Reset() + /// + /// Pause the timer. + /// + public void Stop() + { + if (!IsStarted || IsFinished) { - Timer?.Change(Timeout.Infinite, Timeout.Infinite); - IsStarted = false; - IsPaused = false; - IsFinished = false; - StartPoint = DateTime.Now; - ElapsedTime = TimeSpan.Zero; - UpdateElapsedTime(null); + return; } + IsStarted = true; + IsPaused = true; + IsFinished = false; + } - /// - /// Given a checkpoint name, skip the timer to that checkpoint. - /// - /// The name of the checkpoint to skip to. - public void SkipToCheckPoint(string checkPointName) + /// + /// Reset the timer back to its initial state. + /// + public void Reset() + { + Timer?.Change(Timeout.Infinite, Timeout.Infinite); + IsStarted = false; + IsPaused = false; + IsFinished = false; + StartPoint = DateTime.Now; + ElapsedTime = TimeSpan.Zero; + UpdateElapsedTime(null); + } + + /// + /// Given a checkpoint name, skip the timer to that checkpoint. + /// + /// The name of the checkpoint to skip to. + public void SkipToCheckPoint(string checkPointName) + { + if (CheckPoints.ContainsKey(checkPointName)) { - if (CheckPoints.ContainsKey(checkPointName)) - { - // Sum all the checkpoints up to the checkpoint we want to skip to. - StartPoint = DateTime.Now - TimeSpan.FromSeconds(CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPointName) + 1).Sum(x => x.Value.TotalSeconds)); - UpdateElapsedTime(null); - } + // Sum all the checkpoints up to the checkpoint we want to skip to. + StartPoint = DateTime.Now - TimeSpan.FromSeconds(CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPointName) + 1).Sum(x => x.Value.TotalSeconds)); + UpdateElapsedTime(null); } + } - /// - /// Skip to the next checkpoint. - /// - public void SkipToNextCheckPoint() + /// + /// Skip to the next checkpoint. + /// + public void SkipToNextCheckPoint() + { + if (CheckPoints.Count > 0) { - if (CheckPoints.Count > 0) + // Figure out which checkpoints we are between. Checkpoints should be added in order since we are using a dictionary that just stores the duration of each checkpoint. + foreach (var checkPoint in CheckPoints) { - // Figure out which checkpoints we are between. Checkpoints should be added in order since we are using a dictionary that just stores the duration of each checkpoint. - foreach (var checkPoint in CheckPoints) + // Sum the checkpoints before with this one. + double checkPointStart = CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPoint.Key) + 1).Sum(x => x.Value.TotalSeconds); + // Check if the elapsed time is between the current checkpoint and the next checkpoint. + if (checkPointStart >= ElapsedTime.TotalSeconds) { - // Sum the checkpoints before with this one. - double checkPointStart = CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPoint.Key) + 1).Sum(x => x.Value.TotalSeconds); - // Check if the elapsed time is between the current checkpoint and the next checkpoint. - if (checkPointStart >= ElapsedTime.TotalSeconds) - { - SkipToCheckPoint(checkPoint.Key); - break; - } + SkipToCheckPoint(checkPoint.Key); + break; } } } + } - /// - /// Go back to the previous checkpoint. - /// - public void SkipToPreviousCheckPoint() + /// + /// Go back to the previous checkpoint. + /// + public void SkipToPreviousCheckPoint() + { + if (CheckPoints.Count > 0) { - if (CheckPoints.Count > 0) + // Figure out which checkpoints we are between. Checkpoints should be added in order since we are using a dictionary that just stores the duration of each checkpoint. + foreach (var checkPoint in CheckPoints) { - // Figure out which checkpoints we are between. Checkpoints should be added in order since we are using a dictionary that just stores the duration of each checkpoint. - foreach (var checkPoint in CheckPoints) + // Sum the checkpoints before with this one. + double checkPointStart = CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPoint.Key) + 1).Sum(x => x.Value.TotalSeconds); + // Check if the elapsed time is between the current checkpoint and the next checkpoint. + if (checkPointStart <= ElapsedTime.TotalSeconds) { - // Sum the checkpoints before with this one. - double checkPointStart = CheckPoints.Take(CheckPoints.Keys.ToList().IndexOf(checkPoint.Key) + 1).Sum(x => x.Value.TotalSeconds); - // Check if the elapsed time is between the current checkpoint and the next checkpoint. - if (checkPointStart <= ElapsedTime.TotalSeconds) + // Check if the checkpoint start is zero. This means we haven't reached the first checkpoint yet. + if (checkPointStart == 0) { - // Check if the checkpoint start is zero. This means we haven't reached the first checkpoint yet. - if (checkPointStart == 0) - { - StartPoint = DateTime.Now; - UpdateElapsedTime(null); - break; - } - else - { - SkipToCheckPoint(checkPoint.Key); - break; - } + StartPoint = DateTime.Now; + UpdateElapsedTime(null); + break; } else { - // If we are at the first checkpoint, reset the timer. - Reset(); + SkipToCheckPoint(checkPoint.Key); break; } } - } - } - - /// - /// Updates timer metrics. - /// - /// - /// - private async void UpdateElapsedTime(object? state) - { - // Check if the timer is paused, but if the start time was changed, we need to update the elapsed time. - if (!IsPaused || _startPointChanged) - { - // Check if the timer is finished. - if (DateTime.Now - StartPoint >= EndPoint) + else { - Timer?.Change(Timeout.Infinite, Timeout.Infinite); - IsFinished = true; + // If we are at the first checkpoint, reset the timer. + Reset(); + break; } - ElapsedTime = DateTime.Now - StartPoint; - _startPointChanged = false; - } - if (TimerTickNotifier is not null) - { - await TimerTickNotifier.Invoke(TaskType, ElapsedTime); } } + } - /// - /// Converts a timespan into a short readable string. For example, 1h 30m 20s. - /// - /// The timespan to parse into a short string. - /// - public static string TimeSpanToShortReadableString(TimeSpan timeSpan) + /// + /// Updates timer metrics. + /// + /// + /// + private async void UpdateElapsedTime(object? state) + { + // Check if the timer is paused, but if the start time was changed, we need to update the elapsed time. + if (!IsPaused || _startPointChanged) { - if (timeSpan.TotalSeconds < 1) + // Check if the timer is finished. + if (DateTime.Now - StartPoint >= EndPoint) { - return "0s"; + Timer?.Change(Timeout.Infinite, Timeout.Infinite); + IsFinished = true; } + ElapsedTime = DateTime.Now - StartPoint; + _startPointChanged = false; + } + if (TimerTickNotifier is not null) + { + await TimerTickNotifier.Invoke(TaskType, ElapsedTime); + } + } - var parts = new List(); + /// + /// Converts a timespan into a short readable string. For example, 1h 30m 20s. + /// + /// The timespan to parse into a short string. + /// + public static string TimeSpanToShortReadableString(TimeSpan timeSpan) + { + if (timeSpan.TotalSeconds < 1) + { + return "0s"; + } - if (timeSpan.Hours > 0) - { - parts.Add($"{timeSpan.Hours}h"); - } + var parts = new List(); - if (timeSpan.Minutes > 0) - { - parts.Add($"{timeSpan.Minutes}m"); - } + if (timeSpan.Hours > 0) + { + parts.Add($"{timeSpan.Hours}h"); + } - if (timeSpan.Seconds > 0) - { - parts.Add($"{timeSpan.Seconds}s"); - } + if (timeSpan.Minutes > 0) + { + parts.Add($"{timeSpan.Minutes}m"); + } - return string.Join(" ", parts); + if (timeSpan.Seconds > 0) + { + parts.Add($"{timeSpan.Seconds}s"); } - } + return string.Join(" ", parts); + } } \ No newline at end of file diff --git a/Basestation_Software.Models/Timers/TimerType.cs b/Basestation_Software.Models/Timers/TimerType.cs index 55f2c80..8a7a762 100644 --- a/Basestation_Software.Models/Timers/TimerType.cs +++ b/Basestation_Software.Models/Timers/TimerType.cs @@ -1,10 +1,9 @@ -namespace Basestation_Software.Models.Timers +namespace Basestation_Software.Models.Timers; + +public enum TaskType { - public enum TaskType - { - Autonomy, - Science, - ExtremeDelivery, - EquipmentServicing - } + Autonomy, + Science, + ExtremeDelivery, + EquipmentServicing } \ No newline at end of file diff --git a/Basestation_Software.Web/Core/Services/CookieService.cs b/Basestation_Software.Web/Core/Services/CookieService.cs index 5576bc9..f73967b 100644 --- a/Basestation_Software.Web/Core/Services/CookieService.cs +++ b/Basestation_Software.Web/Core/Services/CookieService.cs @@ -1,79 +1,83 @@ using Microsoft.JSInterop; -namespace Basestation_Software.Web.Core.Services +namespace Basestation_Software.Web.Core.Services; + +public class CookieService { - public class CookieService + // Declare member variables. + private readonly IJSRuntime _JSRuntime; + string expires = ""; + + /// + /// Constructor + /// + /// Implicitly passed in. + public CookieService(IJSRuntime jsRuntime) { - // Declare member variables. - private readonly IJSRuntime _JSRuntime; - string expires = ""; + _JSRuntime = jsRuntime; + ExpireDays = 30; + } - /// - /// Constructor - /// - /// Implicitly passed in. - public CookieService(IJSRuntime jsRuntime) - { - _JSRuntime = jsRuntime; - ExpireDays = 30; - } + /// + /// Stores a cookie in the browser. + /// + /// The key to reference the cookie by later. + /// The calue to store in the cookie + /// The number of days before the cookie expires. + /// + public async Task SetValue(string key, string value, int? days = null) + { + var curExp = (days != null) ? (days > 0 ? DateToUTC(days.Value) : "") : expires; + await SetCookie($"{key}={value}; expires={curExp}; path=/"); + } - /// - /// Stores a cookie in the browser. - /// - /// The key to reference the cookie by later. - /// The calue to store in the cookie - /// The number of days before the cookie expires. - /// - public async Task SetValue(string key, string value, int? days = null) - { - var curExp = (days != null) ? (days > 0 ? DateToUTC(days.Value) : "") : expires; - await SetCookie($"{key}={value}; expires={curExp}; path=/"); - } + /// + /// Gets a cookie from the browser. + /// + /// The name of the cookie. + /// The default value if the cookie isn't found. + /// + public async Task GetValue(string key, string def = "") + { + var cValue = await GetCookie(); + if (string.IsNullOrEmpty(cValue)) return def; - /// - /// Gets a cookie from the browser. - /// - /// The name of the cookie. - /// The default value if the cookie isn't found. - /// - public async Task GetValue(string key, string def = "") + var vals = cValue.Split(';'); + foreach (var val in vals) { - var cValue = await GetCookie(); - if (string.IsNullOrEmpty(cValue)) return def; - - var vals = cValue.Split(';'); - foreach (var val in vals) - if(!string.IsNullOrEmpty(val) && val.IndexOf('=') > 0) - if(val.Substring(0, val.IndexOf('=')).Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) - return val.Substring(val.IndexOf('=') + 1); - return def; + if (!string.IsNullOrEmpty(val) && val.IndexOf('=') > 0) + { + if (val.Substring(0, val.IndexOf('=')).Trim().Equals(key, StringComparison.OrdinalIgnoreCase)) + return val.Substring(val.IndexOf('=') + 1); + } } - /// - /// Sets a cookie. - /// - /// Cookie info. - /// - private async Task SetCookie(string value) - { - await _JSRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{value}\""); - } + return def; + } - /// - /// Gets a cookie. - /// - /// The cookie info. - private async Task GetCookie() - { - return await _JSRuntime.InvokeAsync("eval", $"document.cookie"); - } + /// + /// Sets a cookie. + /// + /// Cookie info. + /// + private async Task SetCookie(string value) + { + await _JSRuntime.InvokeVoidAsync("eval", $"document.cookie = \"{value}\""); + } - public int ExpireDays - { - set => expires = DateToUTC(value); - } + /// + /// Gets a cookie. + /// + /// The cookie info. + private async Task GetCookie() + { + return await _JSRuntime.InvokeAsync("eval", $"document.cookie"); + } - private static string DateToUTC(int days) => DateTime.Now.AddDays(days).ToUniversalTime().ToString("R"); + public int ExpireDays + { + set => expires = DateToUTC(value); } + + private static string DateToUTC(int days) => DateTime.Now.AddDays(days).ToUniversalTime().ToString("R"); } \ No newline at end of file diff --git a/Basestation_Software.Web/Core/Services/GPSWaypointService.cs b/Basestation_Software.Web/Core/Services/GPSWaypointService.cs index 0bb1f09..ef00bde 100644 --- a/Basestation_Software.Web/Core/Services/GPSWaypointService.cs +++ b/Basestation_Software.Web/Core/Services/GPSWaypointService.cs @@ -1,119 +1,118 @@ using Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Web.Core.Services +namespace Basestation_Software.Web.Core.Services; + +public class GPSWaypointService { - public class GPSWaypointService - { - // Injected services. - private readonly HttpClient _HttpClient; - // Declare member variables. - private List _gpsWaypoints = new List(); + // Injected services. + private readonly HttpClient _HttpClient; + // Declare member variables. + private List _gpsWaypoints = []; - // Method delegates and events. - public delegate Task SyncWaypointsCallback(); - private event SyncWaypointsCallback? SyncWaypointsNotifier; + // Method delegates and events. + public delegate Task SyncWaypointsCallback(); + private event SyncWaypointsCallback? SyncWaypointsNotifier; - /// - /// Constructor - /// - /// Implicitly passed in, used to talk to the basestation API. - public GPSWaypointService(HttpClient httpClient) - { - // Assign member variables. - _HttpClient = httpClient; - } + /// + /// Constructor + /// + /// Implicitly passed in, used to talk to the basestation API. + public GPSWaypointService(HttpClient httpClient) + { + // Assign member variables. + _HttpClient = httpClient; + } - /// - /// Refreshes the cached waypoints list from the API database. - /// - /// - public async Task RefreshGPSWaypoints() + /// + /// Refreshes the cached waypoints list from the API database. + /// + /// + public async Task RefreshGPSWaypoints() + { + List? waypoints = await _HttpClient.GetFromJsonAsync>("http://localhost:5000/api/GPSWaypoint"); + if (waypoints is not null) { - List? waypoints = await _HttpClient.GetFromJsonAsync>("http://localhost:5000/api/GPSWaypoint"); - if (waypoints is not null) - { - _gpsWaypoints = waypoints; - } - - // Invoke the callback to refresh page data. - await SyncWaypointsNotifier!.Invoke(); + _gpsWaypoints = waypoints; } - /// - /// Add a callback to get invoked when the waypoints list changes. - /// - /// The method callback to add. - public void SubscribeToWaypointsChanges(SyncWaypointsCallback callback) - { - SyncWaypointsNotifier += callback; - } + // Invoke the callback to refresh page data. + await SyncWaypointsNotifier!.Invoke(); + } - /// - /// Remove a callback from getting invoked when the waypoints list changes. - /// - /// The method callback to remove. - public void UnsubscribeFromWaypointsChanges(SyncWaypointsCallback callback) - { - SyncWaypointsNotifier -= callback; - } + /// + /// Add a callback to get invoked when the waypoints list changes. + /// + /// The method callback to add. + public void SubscribeToWaypointsChanges(SyncWaypointsCallback callback) + { + SyncWaypointsNotifier += callback; + } - /// - /// Add a new waypoint to the database. - /// - /// The waypoint to add. - /// - public async Task AddGPSWaypoint(GPSWaypoint waypoint) - { - // Add the waypoint to the database with the API. - await _HttpClient.PutAsJsonAsync($"http://localhost:5000/api/GPSWaypoint", waypoint); - // Refresh data. - await RefreshGPSWaypoints(); - } + /// + /// Remove a callback from getting invoked when the waypoints list changes. + /// + /// The method callback to remove. + public void UnsubscribeFromWaypointsChanges(SyncWaypointsCallback callback) + { + SyncWaypointsNotifier -= callback; + } - /// - /// Update a waypoint in the database. - /// - /// The waypoint to update. ID must match an existing ID. - /// - public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) - { - // Write the waypoint data to the database with the API. - await _HttpClient.PostAsJsonAsync($"http://localhost:5000/api/GPSWaypoint", waypoint); - // Refresh data. - await RefreshGPSWaypoints(); - } + /// + /// Add a new waypoint to the database. + /// + /// The waypoint to add. + /// + public async Task AddGPSWaypoint(GPSWaypoint waypoint) + { + // Add the waypoint to the database with the API. + await _HttpClient.PutAsJsonAsync($"http://localhost:5000/api/GPSWaypoint", waypoint); + // Refresh data. + await RefreshGPSWaypoints(); + } - /// - /// Given a waypoint ID delete it from the database. - /// - /// The waypoint to delete. - /// - public async Task DeleteGPSWaypoint(GPSWaypoint waypoint) - { - // Delete the waypoint from the database. - await _HttpClient.DeleteAsync($"http://localhost:5000/api/GPSWaypoint/{waypoint.ID}"); - // Refresh data. - await RefreshGPSWaypoints(); - } + /// + /// Update a waypoint in the database. + /// + /// The waypoint to update. ID must match an existing ID. + /// + public async Task UpdateGPSWaypoint(GPSWaypoint waypoint) + { + // Write the waypoint data to the database with the API. + await _HttpClient.PostAsJsonAsync($"http://localhost:5000/api/GPSWaypoint", waypoint); + // Refresh data. + await RefreshGPSWaypoints(); + } - /// - /// Return a reference to the list of GPSWaypoints. - /// - /// - public List GetGPSWaypoints() - { - return _gpsWaypoints; - } + /// + /// Given a waypoint ID delete it from the database. + /// + /// The waypoint to delete. + /// + public async Task DeleteGPSWaypoint(GPSWaypoint waypoint) + { + // Delete the waypoint from the database. + await _HttpClient.DeleteAsync($"http://localhost:5000/api/GPSWaypoint/{waypoint.ID}"); + // Refresh data. + await RefreshGPSWaypoints(); + } - /// - /// Returns the waypoint with the given ID. - /// - /// The ID of the waypoint to retrieve. - /// - public GPSWaypoint? GetGPSWaypoint(int waypointID) - { - return _gpsWaypoints.FirstOrDefault(x => x.ID == waypointID); - } + /// + /// Return a reference to the list of GPSWaypoints. + /// + /// + public List GetGPSWaypoints() + { + return _gpsWaypoints; + } + + /// + /// Returns the waypoint with the given ID. + /// + /// The ID of the waypoint to retrieve. + /// + public GPSWaypoint? GetGPSWaypoint(int waypointID) + { + return _gpsWaypoints.FirstOrDefault(x => x.ID == waypointID); } } \ No newline at end of file diff --git a/Basestation_Software.Web/Core/Services/MapTileService.cs b/Basestation_Software.Web/Core/Services/MapTileService.cs index 99f02f7..45710a0 100644 --- a/Basestation_Software.Web/Core/Services/MapTileService.cs +++ b/Basestation_Software.Web/Core/Services/MapTileService.cs @@ -1,92 +1,91 @@ using Basestation_Software.Models.Geospatial; -namespace Basestation_Software.Web.Core.Services +namespace Basestation_Software.Web.Core.Services; + +public class MapTileService { - public class MapTileService + // Declare member variables. + private readonly HttpClient _HttpClient; + + + /// + /// Constructor + /// + /// Implicitly passed in, used to talk to the basestation API. + public MapTileService(HttpClient httpClient) { - // Declare member variables. - private readonly HttpClient _HttpClient; - + // Assign member variables. + _HttpClient = httpClient; + } - /// - /// Constructor - /// - /// Implicitly passed in, used to talk to the basestation API. - public MapTileService(HttpClient httpClient) - { - // Assign member variables. - _HttpClient = httpClient; - } + /// + /// Add a new tile to the database. + /// + /// The tile to add. + /// + public async Task AddMapTile(MapTile tile) + { + // Add the tile to the database with the API. + await _HttpClient.PutAsJsonAsync($"http://localhost:5000/api/MapTiles", tile); + } - /// - /// Add a new tile to the database. - /// - /// The tile to add. - /// - public async Task AddMapTile(MapTile tile) - { - // Add the tile to the database with the API. - await _HttpClient.PutAsJsonAsync($"http://localhost:5000/api/MapTiles", tile); - } + /// + /// Update a tile in the database. + /// + /// The tile to update. ID must match an existing ID. + /// + public async Task UpdateMapTile(MapTile tile) + { + // Write the tile data to the database with the API. + await _HttpClient.PostAsJsonAsync($"http://localhost:5000/api/MapTiles", tile); + } + + /// + /// Given a tile ID delete it from the database. + /// + /// The tile to delete. + /// + public async Task DeleteMapTile(MapTile tile) + { + // Delete the tile from the database. + await _HttpClient.DeleteAsync($"http://localhost:5000/api/MapTiles/{tile.ID}"); + } - /// - /// Update a tile in the database. - /// - /// The tile to update. ID must match an existing ID. - /// - public async Task UpdateMapTile(MapTile tile) + /// + /// Returns the tile image with the given x, y, z + /// + /// + /// + /// + /// + public async Task GetMapTileImage(int x, int y, int z) + { + try { - // Write the tile data to the database with the API. - await _HttpClient.PostAsJsonAsync($"http://localhost:5000/api/MapTiles", tile); + return await _HttpClient.GetFromJsonAsync($"http://localhost:5000/api/MapTiles/{z}/{y}/{x}.png"); } - - /// - /// Given a tile ID delete it from the database. - /// - /// The tile to delete. - /// - public async Task DeleteMapTile(MapTile tile) + catch (Exception) { - // Delete the tile from the database. - await _HttpClient.DeleteAsync($"http://localhost:5000/api/MapTiles/{tile.ID}"); + return null; } + } - /// - /// Returns the tile image with the given x, y, z - /// - /// - /// - /// - /// - public async Task GetMapTileImage(int x, int y, int z) + /// + /// Returns the tile with the given x, y, z + /// + /// + /// + /// + /// + public async Task GetMapTile(int x, int y, int z) + { + try { - try - { - return await _HttpClient.GetFromJsonAsync($"http://localhost:5000/api/MapTiles/{z}/{y}/{x}.png"); - } - catch (Exception) - { - return null; - } + return await _HttpClient.GetFromJsonAsync($"http://localhost:5000/api/MapTiles/{z}/{y}/{x}"); } - - /// - /// Returns the tile with the given x, y, z - /// - /// - /// - /// - /// - public async Task GetMapTile(int x, int y, int z) + catch (Exception) { - try - { - return await _HttpClient.GetFromJsonAsync($"http://localhost:5000/api/MapTiles/{z}/{y}/{x}"); - } - catch (Exception) - { - return null; - } + return null; } } } \ No newline at end of file diff --git a/Basestation_Software.Web/Core/Services/TaskTimerService.cs b/Basestation_Software.Web/Core/Services/TaskTimerService.cs index 9cb2add..ba91cc0 100644 --- a/Basestation_Software.Web/Core/Services/TaskTimerService.cs +++ b/Basestation_Software.Web/Core/Services/TaskTimerService.cs @@ -1,147 +1,142 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Basestation_Software.Models.Timers; -namespace Basestation_Software.Web.Core.Services +namespace Basestation_Software.Web.Core.Services; + +public class TaskTimerService { - public class TaskTimerService - { - // Declare member variables. - private List _taskTimers = new List(); + // Declare member variables. + private List _taskTimers = []; - // Create a dictionary of event handlers for updating the UIs. - public delegate Task TimerTickCallback(TimeSpan elapsedTime); - public Dictionary TimerTickNotifiers = new Dictionary(); + // Create a dictionary of event handlers for updating the UIs. + public delegate Task TimerTickCallback(TimeSpan elapsedTime); + public Dictionary TimerTickNotifiers = []; - /// - /// Constructor. - /// - public TaskTimerService() + /// + /// Constructor. + /// + public TaskTimerService() + { + // Loop through the task timer types enum. + foreach (TaskType TaskType in Enum.GetValues(typeof(TaskType))) { - // Loop through the task timer types enum. - foreach (TaskType TaskType in Enum.GetValues(typeof(TaskType))) + // Check if a task timer for the current type exists. + TaskTimer? taskTimer = GetTaskTimer(TaskType); + // Check if the timer is null. + if (taskTimer == null) { - // Check if a task timer for the current type exists. - TaskTimer? taskTimer = GetTaskTimer(TaskType); - // Check if the timer is null. - if (taskTimer == null) + // Create a new timer. + TaskTimer newTimer = new(OnTimerTick, TaskType); + // Configure the timer. + switch (TaskType) { - // Create a new timer. - TaskTimer newTimer = new TaskTimer(OnTimerTick, TaskType); - // Configure the timer. - switch (TaskType) - { - case TaskType.Autonomy: - // Total time for the task. - newTimer.EndPoint = TimeSpan.FromSeconds(80); - // Add checkpoints. - newTimer.CheckPoints = new Dictionary - { - { "Setup", TimeSpan.FromSeconds(10) }, - { "Autonomy Task", TimeSpan.FromSeconds(60) }, - { "PackUp", TimeSpan.FromSeconds(10) } - }; - break; - case TaskType.Science: - // Total time for the task. - newTimer.EndPoint = TimeSpan.FromMinutes(50); - // Add checkpoints. - newTimer.CheckPoints = new Dictionary - { - { "Setup", TimeSpan.FromMinutes(10) }, - { "Science Task", TimeSpan.FromMinutes(30) }, - { "PackUp", TimeSpan.FromMinutes(10) }, - }; - break; - case TaskType.ExtremeDelivery: - // Total time for the task. - newTimer.EndPoint = TimeSpan.FromMinutes(80); - // Add checkpoints. - newTimer.CheckPoints = new Dictionary - { - { "Setup", TimeSpan.FromMinutes(10) }, - { "Extreme Retrieval/Delivery Task", TimeSpan.FromMinutes(60) }, - { "PackUp", TimeSpan.FromMinutes(10) }, - }; - break; - case TaskType.EquipmentServicing: - // Total time for the task. - newTimer.EndPoint = TimeSpan.FromMinutes(50); - // Add checkpoints. - newTimer.CheckPoints = new Dictionary - { - { "Setup", TimeSpan.FromMinutes(10) }, - { "Equipment Servicing Task", TimeSpan.FromMinutes(30) }, - { "PackUp", TimeSpan.FromMinutes(10) }, - }; - break; - default: - newTimer.EndPoint = TimeSpan.FromMinutes(1); - break; - } - // Add the timer to the service. - AddTaskTimer(newTimer); + case TaskType.Autonomy: + // Total time for the task. + newTimer.EndPoint = TimeSpan.FromSeconds(80); + // Add checkpoints. + newTimer.CheckPoints = new Dictionary + { + { "Setup", TimeSpan.FromSeconds(10) }, + { "Autonomy Task", TimeSpan.FromSeconds(60) }, + { "PackUp", TimeSpan.FromSeconds(10) } + }; + break; + case TaskType.Science: + // Total time for the task. + newTimer.EndPoint = TimeSpan.FromMinutes(50); + // Add checkpoints. + newTimer.CheckPoints = new Dictionary + { + { "Setup", TimeSpan.FromMinutes(10) }, + { "Science Task", TimeSpan.FromMinutes(30) }, + { "PackUp", TimeSpan.FromMinutes(10) }, + }; + break; + case TaskType.ExtremeDelivery: + // Total time for the task. + newTimer.EndPoint = TimeSpan.FromMinutes(80); + // Add checkpoints. + newTimer.CheckPoints = new Dictionary + { + { "Setup", TimeSpan.FromMinutes(10) }, + { "Extreme Retrieval/Delivery Task", TimeSpan.FromMinutes(60) }, + { "PackUp", TimeSpan.FromMinutes(10) }, + }; + break; + case TaskType.EquipmentServicing: + // Total time for the task. + newTimer.EndPoint = TimeSpan.FromMinutes(50); + // Add checkpoints. + newTimer.CheckPoints = new Dictionary + { + { "Setup", TimeSpan.FromMinutes(10) }, + { "Equipment Servicing Task", TimeSpan.FromMinutes(30) }, + { "PackUp", TimeSpan.FromMinutes(10) }, + }; + break; + default: + newTimer.EndPoint = TimeSpan.FromMinutes(1); + break; } - - // Add the timer tick callback to the dictionary. - TimerTickNotifiers.Add(TaskType, null); + // Add the timer to the service. + AddTaskTimer(newTimer); } - } - /// - /// Runs when a timer ticks. - /// - /// The task type. - /// The current time elapsed in the task. - /// - private async Task OnTimerTick(TaskType timerName, TimeSpan timeElapsed) - { - // Invoke the timer tick callback. - if (TimerTickNotifiers.ContainsKey(timerName) && TimerTickNotifiers[timerName] is not null) - { - await TimerTickNotifiers[timerName]!.Invoke(timeElapsed); - } + // Add the timer tick callback to the dictionary. + TimerTickNotifiers.Add(TaskType, null); } + } - /// - /// Add a task timer to the list of task timers. - /// - /// The task timer object. - public void AddTaskTimer(TaskTimer taskTimer) + /// + /// Runs when a timer ticks. + /// + /// The task type. + /// The current time elapsed in the task. + /// + private async Task OnTimerTick(TaskType timerName, TimeSpan timeElapsed) + { + // Invoke the timer tick callback. + if (TimerTickNotifiers.ContainsKey(timerName) && TimerTickNotifiers[timerName] is not null) { - _taskTimers.Add(taskTimer); + await TimerTickNotifiers[timerName]!.Invoke(timeElapsed); } + } - /// - /// Remove a task timer from the list of task timers. - /// - /// The tasktimer object type to remove. - public void RemoveTaskTimer(TaskType taskTimerType) + /// + /// Add a task timer to the list of task timers. + /// + /// The task timer object. + public void AddTaskTimer(TaskTimer taskTimer) + { + _taskTimers.Add(taskTimer); + } + + /// + /// Remove a task timer from the list of task timers. + /// + /// The tasktimer object type to remove. + public void RemoveTaskTimer(TaskType taskTimerType) + { + TaskTimer? taskTimer = GetTaskTimer(taskTimerType); + if (taskTimer != null) { - TaskTimer? taskTimer = GetTaskTimer(taskTimerType); - if (taskTimer != null) - { - _taskTimers.RemoveAll(t => t == taskTimer); - } + _taskTimers.RemoveAll(t => t == taskTimer); } + } - /// - /// Get a task timer from the list of task timers. - /// - /// The name of the task timer to retrieve. - /// - public TaskTimer? GetTaskTimer(TaskType timerType) + /// + /// Get a task timer from the list of task timers. + /// + /// The name of the task timer to retrieve. + /// + public TaskTimer? GetTaskTimer(TaskType timerType) + { + foreach (TaskTimer taskTimer in _taskTimers) { - foreach (TaskTimer taskTimer in _taskTimers) + if (taskTimer.TaskType == timerType) { - if (taskTimer.TaskType == timerType) - { - return taskTimer; - } + return taskTimer; } - return null; } + return null; } } \ No newline at end of file diff --git a/Basestation_Software.Web/Program.cs b/Basestation_Software.Web/Program.cs index 4e8c52f..d15131f 100644 --- a/Basestation_Software.Web/Program.cs +++ b/Basestation_Software.Web/Program.cs @@ -3,16 +3,16 @@ using Blazored.Toast; using Radzen; +#pragma warning disable IDE0211 // Convert to 'Program.Main' style program var builder = WebApplication.CreateBuilder(args); -#pragma warning restore IDE0211 // Convert to 'Program.Main' style program // Add services to the container. builder.Services.AddRazorComponents() .AddInteractiveServerComponents() .AddInteractiveWebAssemblyComponents(); builder.Services.AddServerSideBlazor() - .AddCircuitOptions(option => - { + .AddCircuitOptions(option => + { option.DetailedErrors = true; option.DisconnectedCircuitRetentionPeriod = TimeSpan.FromSeconds(10); }) From e206d992df994f5055a72ae0658259278f475ee1 Mon Sep 17 00:00:00 2001 From: OcelotEmpire Date: Mon, 23 Sep 2024 17:17:20 -0500 Subject: [PATCH 13/15] changed callback parameter type to RoveCommPacket --- .../RoveComm/RoveCommPacket.cs | 2 +- .../RoveComm/RoveCommUtils.cs | 275 ++++++++++-------- .../Core/Components/RoveCommExample.razor | 33 ++- .../Core/Services/RoveComm/RoveCommService.cs | 30 +- .../Core/Services/RoveComm/RoveCommTCP.cs | 2 +- .../Core/Services/RoveComm/RoveCommUDP.cs | 2 +- 6 files changed, 196 insertions(+), 148 deletions(-) diff --git a/Basestation_Software.Models/RoveComm/RoveCommPacket.cs b/Basestation_Software.Models/RoveComm/RoveCommPacket.cs index 9d5e61d..32b204f 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommPacket.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommPacket.cs @@ -29,7 +29,7 @@ public RoveCommPacket(int dataId, List data) } public RoveCommPacket(int dataId, int dataCount) : - this(dataId, new List(dataCount)) + this(dataId, new List(new T[dataCount])) { } public RoveCommHeader GetHeader() diff --git a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs index b86859b..86a1171 100644 --- a/Basestation_Software.Models/RoveComm/RoveCommUtils.cs +++ b/Basestation_Software.Models/RoveComm/RoveCommUtils.cs @@ -20,6 +20,12 @@ public RoveCommException(string message) : base(message) { } public RoveCommException(string message, Exception inner) : base(message, inner) { } } +/// +/// A function to be called by RoveComm when a relevant RoveCommPacket is received. +/// +/// The Data field of the received RoveCommPacket. +public delegate Task RoveCommCallback(RoveCommPacket packet); + /// /// Helper functions used by RoveComm internally. /// @@ -111,21 +117,62 @@ public static bool FindBoardByName(string boardName, out RoveCommBoardDesc? boar /// Query board and packet info from the Manifest by their names. /// /// The name of the board. - /// The name of the packet. + /// The name of the packet. /// The RoveCommBoardDesc to fill in with information, if the board is found. /// The RoveCommPacketDesc to fill in with information, if the packet is found. - /// True only if both the board and the packet were found in the manifest. - public static bool FindByBoardAndDataID(string boardName, string dataIdString, out RoveCommBoardDesc? boardDesc, out RoveCommPacketDesc? packetDesc) + /// True only if both the board and the packet were found in the Manifest. + public static bool FindDataIDByName(string boardName, string packetName, out RoveCommBoardDesc? boardDesc, out RoveCommPacketDesc? packetDesc) { packetDesc = null; return RoveCommManifest.Boards.TryGetValue(boardName, out boardDesc) && ( - boardDesc.Commands.TryGetValue(dataIdString, out packetDesc) - || boardDesc.Telemetry.TryGetValue(dataIdString, out packetDesc) - || boardDesc.Errors.TryGetValue(dataIdString, out packetDesc) + boardDesc.Commands.TryGetValue(packetName, out packetDesc) + || boardDesc.Telemetry.TryGetValue(packetName, out packetDesc) + || boardDesc.Errors.TryGetValue(packetName, out packetDesc) ); } + /// + /// Query board and packet names from the Manifest by its DataID. + /// + /// The DataID to search for. + /// The board name, if the DataID is found. + /// The packet name, if the DataID is found. + /// True only if the DataID was found in the Manifest. + public static bool FindNameByDataID(int dataId, out string? boardName, out string? packetName) + { + boardName = null; + packetName = null; + foreach (var (bname, bdesc) in RoveCommManifest.Boards) + { + foreach (var (pname, pdesc) in bdesc.Commands) + { + if (pdesc.DataID == dataId) { + boardName = bname; + packetName = pname; + return true; + } + } + foreach (var (pname, pdesc) in bdesc.Telemetry) + { + if (pdesc.DataID == dataId) { + boardName = bname; + packetName = pname; + return true; + } + } + foreach (var (pname, pdesc) in bdesc.Errors) + { + if (pdesc.DataID == dataId) { + boardName = bname; + packetName = pname; + return true; + } + } + } + return false; + } + /// /// Read a RoveCommPacket header from the given byte buffer. @@ -216,86 +263,85 @@ public static RoveCommPacket ParsePacket(ReadOnlySpan data) { // SEXY PATTERN MATCHING case List packetData: + { + var casted = MemoryMarshal.Cast(dataBuf); + for (int i = 0; i < header.DataCount; i++) { - var casted = MemoryMarshal.Cast(dataBuf); - for (int i = 0; i < header.DataCount; i++) packetData.Add(casted[i]); - break; + packetData[i] = casted[i]; } + break; + } case List packetData: + { + // No cast necessary -- already bytes. + for (int i = 0; i < header.DataCount; i++) { - // No cast necessary -- already bytes. - for (int i = 0; i < header.DataCount; i++) packetData.Add(dataBuf[i]); - break; + packetData[i] = dataBuf[i]; } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadInt16BigEndian(dataBuf.Slice(i * 2, 2))); - } - - break; + packetData[i] = BinaryPrimitives.ReadInt16BigEndian(dataBuf.Slice(i * 2, 2)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadUInt16BigEndian(dataBuf.Slice(i * 2, 2))); - } - - break; + packetData[i] = BinaryPrimitives.ReadUInt16BigEndian(dataBuf.Slice(i * 2, 2)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadInt32BigEndian(dataBuf.Slice(i * 4, 4))); - } - - break; + packetData[i] = BinaryPrimitives.ReadInt32BigEndian(dataBuf.Slice(i * 4, 4)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadUInt32BigEndian(dataBuf.Slice(i * 4, 4))); - } - - break; + packetData[i] = BinaryPrimitives.ReadUInt32BigEndian(dataBuf.Slice(i * 4, 4)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadSingleBigEndian(dataBuf.Slice(i * 4, 4))); - } - - break; + packetData[i] = BinaryPrimitives.ReadSingleBigEndian(dataBuf.Slice(i * 4, 4)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - packetData.Add(BinaryPrimitives.ReadDoubleBigEndian(dataBuf.Slice(i * 8, 8))); - } - - break; + packetData[i] = BinaryPrimitives.ReadDoubleBigEndian(dataBuf.Slice(i * 8, 8)); } + break; + } case List packetData: + { + for (int i = 0; i < header.DataCount; i++) { - for (int i = 0; i < header.DataCount; i++) - { - // Careful: C# stores chars in UTF-16, so they are 2 bytes wide. - // This can cause encoding errors if converting byte[] directly to string. - packetData.Add((char)dataBuf[i]); - } - - break; + // Careful: C# stores chars in UTF-16, so they are 2 bytes wide. + // This can cause encoding errors if converting byte[] directly to string. + packetData[i] = (char)dataBuf[i]; } + break; + } default: - { - throw new RoveCommException("Failed to parse RoveCommPacket: invalid data type."); - } + { + throw new RoveCommException("Failed to parse RoveCommPacket: invalid data type."); + } } return packet; @@ -313,7 +359,7 @@ public static byte[] PackPacket(RoveCommPacket packet) int dataSize = packet.DataCount * DataTypeSize(packet.DataType); if (dataSize > RoveCommConsts.MaxDataSize) { - throw new RoveCommException("Failed to pack RoveCommPacket: payload exceeds max data size."); + throw new RoveCommException("Failed to pack RoveCommPacket: packet exceeds max data size."); } byte[] dataBuf = new byte[RoveCommConsts.HeaderSize + dataSize]; PackPacket(dataBuf, packet); @@ -329,7 +375,7 @@ public static void PackPacket(Span dest, RoveCommPacket packet) } if (packetSize > RoveCommConsts.HeaderSize + RoveCommConsts.MaxDataSize) { - throw new RoveCommException("Failed to pack RoveCommPacket: payload exceeds max data size."); + throw new RoveCommException("Failed to pack RoveCommPacket: packet exceeds max data size."); } // Pack header in network byte order. PackHeader(dest, packet.GetHeader()); @@ -339,91 +385,82 @@ public static void PackPacket(Span dest, RoveCommPacket packet) switch (packet.Data) { case List packetData: + { + var casted = MemoryMarshal.Cast(dataBuf); + for (int i = 0; i < packet.DataCount; i++) { - var casted = MemoryMarshal.Cast(dataBuf); - for (int i = 0; i < packet.DataCount; i++) - { - casted[i] = packetData[i]; - } - - break; + casted[i] = packetData[i]; } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - dataBuf[0] = packetData[0]; - } - - break; + dataBuf[0] = packetData[0]; } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); - } - - break; + BinaryPrimitives.WriteInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteUInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); - } - - break; + BinaryPrimitives.WriteUInt16BigEndian(dataBuf.Slice(i * 2, 2), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); - } - - break; + BinaryPrimitives.WriteInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteUInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); - } - - break; + BinaryPrimitives.WriteUInt32BigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteSingleBigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); - } - - break; + BinaryPrimitives.WriteSingleBigEndian(dataBuf.Slice(i * 4, 4), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - BinaryPrimitives.WriteDoubleBigEndian(dataBuf.Slice(i * 8, 8), packetData[i]); - } - - break; + BinaryPrimitives.WriteDoubleBigEndian(dataBuf.Slice(i * 8, 8), packetData[i]); } + break; + } case List packetData: + { + for (int i = 0; i < packet.DataCount; i++) { - for (int i = 0; i < packet.DataCount; i++) - { - dataBuf[i] = (byte)packetData[i]; - } - - break; + dataBuf[i] = (byte)packetData[i]; } + break; + } default: - { - throw new RoveCommException("Failed to pack RoveCommPacket: invalid data type."); - } + { + throw new RoveCommException("Failed to pack RoveCommPacket: invalid data type."); + } } } } diff --git a/Basestation_Software.Web/Core/Components/RoveCommExample.razor b/Basestation_Software.Web/Core/Components/RoveCommExample.razor index 4ce01b1..47f8b39 100644 --- a/Basestation_Software.Web/Core/Components/RoveCommExample.razor +++ b/Basestation_Software.Web/Core/Components/RoveCommExample.razor @@ -11,8 +11,9 @@
- - +
+ +

@_message

@if (!_dataLoaded) @@ -47,9 +48,18 @@ protected override async Task OnInitializedAsync() { - // Subscribe to all DataIDs (only for INT32_T packets). + // Subscribe to all DataIDs for each type. + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); _RoveCommService.On(0, AddMessage); - _RoveCommService.On("Core", "DriveLeftRight", async (payload) => + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); + _RoveCommService.On(0, AddMessage); + + _RoveCommService.On("Core", "DriveLeftRight", async (packet) => { _messages.Add("Received DriveLeftRight packet."); _dataLoaded = true; @@ -59,9 +69,16 @@ await InvokeAsync(StateHasChanged); } - public async Task AddMessage(List payload) + public async Task AddMessage(RoveCommPacket packet) { - _messages.Add($"Received Packet. Data: {string.Join(", ", payload)}"); + if (RoveCommUtils.FindNameByDataID(packet.DataID, out string? boardName, out string? packetName)) + { + _messages.Add($"Received {packetName} from {boardName}. Data: {string.Join(", ", packet.Data)}"); + } + else + { + _messages.Add($"Received Packet with unknown DataID {packet.DataID}. Data: {string.Join(", ", packet.Data)}"); + } _dataLoaded = true; await InvokeAsync(StateHasChanged); } @@ -78,7 +95,7 @@ { _message = "Waiting..."; _isDisabled = true; - var result = await _RoveCommService.Listen(0, 3000); + var result = await _RoveCommService.Listen("Core", "DriveLeftRight", 3000); if (result is not null) { _message = "Received!"; @@ -94,7 +111,7 @@ { await _RoveCommService.SendAsync("Core", "DriveLeftRight", [5.5f, 5.5f]); // For testing, also send a copy to the loopback address, since RoveComm will send the packet to 192.168.2.110. - RoveCommUtils.FindByBoardAndDataID("Core", "DriveLeftRight", out var board, out var packet); + RoveCommUtils.FindDataIDByName("Core", "DriveLeftRight", out var board, out var packet); await _RoveCommService.SendAsync(packet!.DataID, [5.5f, 5.5f], "127.0.0.1"); } diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs index 1dc32ea..295c1df 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommService.cs @@ -2,12 +2,6 @@ namespace Basestation_Software.Web.Core.Services.RoveComm; -/// -/// A function to be called by RoveComm when a relevant RoveCommPacket is received. -/// -/// The Data field of the received RoveCommPacket. -public delegate Task RoveCommCallback(List payload); - public class RoveCommService : IHostedService { @@ -62,7 +56,7 @@ public void On(int dataId, RoveCommCallback handler) /// public void On(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + RoveCommUtils.FindDataIDByName(boardName, dataIdString, out var boardDesc, out var packetDesc); if (boardDesc is null) { throw new RoveCommException($"Failed to subscribe to RoveComm: {boardName} Board not found in RoveCommManifest."); @@ -116,7 +110,7 @@ public void Clear(int dataId, RoveCommCallback handler) /// public void Clear(string boardName, string dataIdString, RoveCommCallback handler) { - RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + RoveCommUtils.FindDataIDByName(boardName, dataIdString, out var boardDesc, out var packetDesc); if (boardDesc is null) { throw new RoveCommException($"Failed to unsubscribe from RoveComm: {boardName} Board not found in RoveCommManifest."); @@ -188,7 +182,7 @@ public async Task SendAsync(int dataId, List data, string ip, bool r /// public bool Send(string boardName, string commandName, List data, bool reliable = false) { - RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + RoveCommUtils.FindDataIDByName(boardName, commandName, out var boardDesc, out var packetDesc); if (boardDesc is null) { throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); @@ -225,7 +219,7 @@ public bool Send(string boardName, string commandName, List data, bool rel /// public async Task SendAsync(string boardName, string commandName, List data, bool reliable = false, CancellationToken cancelToken = default) { - RoveCommUtils.FindByBoardAndDataID(boardName, commandName, out var boardDesc, out var packetDesc); + RoveCommUtils.FindDataIDByName(boardName, commandName, out var boardDesc, out var packetDesc); if (boardDesc is null) { throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); @@ -254,13 +248,13 @@ public async Task SendAsync(string boardName, string commandName, List< ///
/// The DataID to listen for. /// The number of milliseconds before returning null. - /// The payload if a packet is received within the timeout, null if none. - public async Task?> Listen(int dataId, int timeout = 30_000) + /// The RoveCommPacket if received within the timeout, null if none. + public async Task?> Listen(int dataId, int timeout = 30_000) { - var promise = new TaskCompletionSource>(); - RoveCommCallback callback = async (payload) => + var promise = new TaskCompletionSource>(); + RoveCommCallback callback = async (packet) => { - promise.SetResult(payload); + promise.SetResult(packet); await Task.CompletedTask; }; @@ -290,10 +284,10 @@ public async Task SendAsync(string boardName, string commandName, List< /// The name of the board as shown in the Manifest. /// The name of the Telemetry or Error message as shown in the Manifest. /// The number of milliseconds before returning null. - /// The payload if a packet is received within the timeout, null if none. - public async Task?> Listen(string boardName, string dataIdString, int timeout = 30_000) + /// The RoveCommPacket if received within the timeout, null if none. + public async Task?> Listen(string boardName, string dataIdString, int timeout = 30_000) { - RoveCommUtils.FindByBoardAndDataID(boardName, dataIdString, out var boardDesc, out var packetDesc); + RoveCommUtils.FindDataIDByName(boardName, dataIdString, out var boardDesc, out var packetDesc); if (boardDesc is null) { throw new RoveCommException($"Failed to send RoveCommPacket: {boardName} Board not found in RoveCommManifest."); diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index d64170e..a37b189 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -17,7 +17,7 @@ public class RoveCommTCP private class RoveCommEmitter { public event RoveCommCallback? Notifier; - public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet.Data); + public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet); } private readonly Dictionary> _callbacksInt8 = []; diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs index f2ac283..2c8604a 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommUDP.cs @@ -16,7 +16,7 @@ public class RoveCommUDP : IDisposable private class RoveCommEmitter { public event RoveCommCallback? Notifier; - public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet.Data); + public void Invoke(RoveCommPacket packet) => Notifier?.Invoke(packet); } private readonly Dictionary> _callbacksInt8 = []; From b35fdc7c1afa948df1263f4e7cb4aaec54aa9ddf Mon Sep 17 00:00:00 2001 From: Donovan Bale Date: Tue, 24 Sep 2024 23:45:52 -0500 Subject: [PATCH 14/15] docker port publishing configuration maps properly --- .devcontainer/devcontainer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 80075d9..1fa5705 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,8 +6,8 @@ "--name", "Basestation_Software", // Configure container to use the same network stack as the host machine. - "--network", - "host", + // "--network", + // "host", // Grants permission to the container to access USBs. "--privileged" ], From 99d8f97a40b3cae3efd82f9df7a9a37d79edcc0a Mon Sep 17 00:00:00 2001 From: OcelotEmpire Date: Mon, 30 Sep 2024 04:45:50 +0000 Subject: [PATCH 15/15] attempted to fix TCP port issue; made it worse :( --- .../Core/Services/RoveComm/RoveCommTCP.cs | 147 +++++++++++++++--- 1 file changed, 129 insertions(+), 18 deletions(-) diff --git a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs index a37b189..523ab50 100644 --- a/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs +++ b/Basestation_Software.Web/Core/Services/RoveComm/RoveCommTCP.cs @@ -6,12 +6,23 @@ namespace Basestation_Software.Web.Core.Services.RoveComm; public class RoveCommTCP { - public static readonly int ConnectionTimeout = 10_000; + public static readonly int TCPConnectionTimeout = 10_000; + public static readonly int TCPMaxClients = 10; + public bool Running { get; private set; } public int Port { get; private set; } - private readonly List _connections = []; + + // TCP connections with RoveComm acting as a client. + private TcpClient?[] _outgoing; + // TCP connections with RoveComm acting as a server. + private List _incoming; + // Stops _removeDisconnectedClients() from removing clients while they're is connecting. + private List _connecting; + + // The socket listening for incoming connections. private TcpListener? _TCPServer; + private readonly ILogger? _logger; private class RoveCommEmitter @@ -34,6 +45,9 @@ public RoveCommTCP(int port, ILogger? logger = null) { Port = port; _logger = logger; + _incoming = new List(); + _outgoing = new TcpClient[TCPMaxClients]; + _connecting = new List(); } public RoveCommTCP(ILogger? logger = null) : this(RoveCommConsts.TCPPort, logger) { } @@ -70,7 +84,7 @@ public void Begin(CancellationToken cancelToken) if (_TCPServer!.Pending()) { TcpClient client = await _TCPServer!.AcceptTcpClientAsync(cancelToken); - _connections.Add(client); + _incoming.Add(client); _logger?.LogInformation("Accepted connection from {Remote}.", client.Client.RemoteEndPoint); } // Read packets and trigger callbacks. @@ -103,12 +117,18 @@ public void Stop() } Running = false; - // Close all connections. - foreach (var connection in _connections) + // Close all incoming connections. + foreach (var connection in _incoming) { connection.Close(); } - _connections.Clear(); + _incoming.Clear(); + // Close all outgoing connections. + for (int i = 0; i < TCPMaxClients; i++) + { + _outgoing[i]?.Close(); + _outgoing[i] = null; + } // Close listening socket. try { @@ -122,11 +142,32 @@ public void Stop() _logger?.LogInformation("Closed RoveComm TCP."); } + // Iterate through incoming and outgoing connections. + private IEnumerable _iterateConnections() + { + foreach (var connection in _incoming) + { + yield return connection; + } + foreach (var connection in _outgoing) + { + if (connection is not null) + { + yield return connection; + } + } + } + // Find existing connection in conneciton list. private TcpClient? _findExisting(IPEndPoint remote) { - foreach (var connection in _connections) + foreach (var connection in _iterateConnections()) { + if (connection is null) + { + continue; + } + var connectionEp = connection.Client.RemoteEndPoint as IPEndPoint; if (connectionEp is null) { @@ -144,11 +185,12 @@ public void Stop() // Remove all connections that have disconnected. private void _removeDisconnectedClients() { - _connections.RemoveAll(connection => - { - if (!connection.Connected) + // Remove all closed incoming connections. + _incoming.RemoveAll((connection) => { + if (!_connecting.Contains(connection) && connection.Client.Connected) { _logger?.LogInformation("Disconnected from {Remote}.", connection.Client.RemoteEndPoint as IPEndPoint); + connection.Dispose(); return true; } else @@ -156,6 +198,38 @@ private void _removeDisconnectedClients() return false; } }); + // Remove all closed outgoing connections. + for (int i = 0; i < TCPMaxClients; i++) + { + var connection = _outgoing[i]; + if (connection is not null && !_connecting.Contains(connection) && !connection.Client.Connected) + { + _logger?.LogInformation("Disconnected from {Remote}.", connection.Client.RemoteEndPoint as IPEndPoint); + connection.Dispose(); + _outgoing[i] = null; + } + }; + } + + // Create a new TcpClient at the first available spot in the list. + private TcpClient? _addNewClient() + { + int freeSpace = Array.IndexOf(_outgoing, null); + if (freeSpace != -1) + { + // Create a new TcpClient with the local endpoint. + // TODO: Add a way to select network interface? + IPAddress localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; + int localPort = Port + freeSpace + 1; + IPEndPoint localEndPoint = new IPEndPoint(localIP, localPort); + TcpClient client = new TcpClient(localEndPoint); + _outgoing[freeSpace] = client; + return client; + } + else + { + return null; + } } /// @@ -183,14 +257,36 @@ public bool Send(RoveCommPacket packet, string ip, int port) if (client is null) { _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); - client = new TcpClient(AddressFamily.InterNetwork); - if (!client.ConnectAsync(dest).Wait(TimeSpan.FromMilliseconds(ConnectionTimeout))) + try + { + client = _addNewClient(); + if (client is null) + { + _logger?.LogError("Failed to connect to remote host: Too many TCP connections open."); + return false; + } + + _connecting.Add(client); + if (!client.ConnectAsync(dest).Wait(TimeSpan.FromMilliseconds(TCPConnectionTimeout))) + { + _logger?.LogError("Failed to connect to remote host: The operation has timed out."); + return false; + } + } + catch (Exception e) { - _logger?.LogError("Failed to connect to remote host: The operation has timed out."); + _logger?.LogError("Failed to connect to remote host: {Error}", e.Message); return false; } + finally + { + if (client is not null) + { + _connecting.Remove(client); + } + } + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); - _connections.Add(client); } // Write the packet to the client's NetworkStream. try @@ -230,19 +326,34 @@ public async Task SendAsync(RoveCommPacket packet, string ip, int po // If no existing connection was found, open a new one. if (client is null) { - client = new TcpClient(AddressFamily.InterNetwork); _logger?.LogInformation("Attempting to establish a connection with {Dest}.", dest); + try { - await client.ConnectAsync(dest).WaitAsync(TimeSpan.FromMilliseconds(ConnectionTimeout), cancelToken); + client = _addNewClient(); + if (client is null) + { + _logger?.LogError("Failed to connect to remote host: Too many TCP connections open."); + return false; + } + + _connecting.Add(client); + await client.ConnectAsync(dest).WaitAsync(TimeSpan.FromMilliseconds(TCPConnectionTimeout), cancelToken); } catch (Exception e) { _logger?.LogError("Failed to connect to remote host: {Error}", e.Message); return false; } + finally + { + if (client is not null) + { + _connecting.Remove(client); + } + } + _logger?.LogInformation("Established connection with {Remote}.", client.Client.RemoteEndPoint as IPEndPoint); - _connections.Add(client); } // Write the packet to the client's NetworkStream. try @@ -272,7 +383,7 @@ public async Task ReceiveAndCallback(CancellationToken cancelToken = default) _removeDisconnectedClients(); - foreach (var connection in _connections) + foreach (var connection in _iterateConnections()) { try {