From 1edb84c1f8af7f8569500b58c1df512276785b1b Mon Sep 17 00:00:00 2001 From: Pranitrane Date: Sun, 9 Aug 2026 18:01:49 +0530 Subject: [PATCH] feat: implement real-time performance overlay (#168) --- backend/__pycache__/debug_log.cpython-314.pyc | Bin 0 -> 2308 bytes .../ai/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 157 bytes .../openai_service.cpython-314.pyc | Bin 0 -> 3953 bytes .../ai/__pycache__/workflow.cpython-314.pyc | Bin 0 -> 13096 bytes .../api/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 158 bytes .../api/__pycache__/router.cpython-314.pyc | Bin 0 -> 1527 bytes .../v1/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 161 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 171 bytes .../__pycache__/agents.cpython-314.pyc | Bin 0 -> 5460 bytes .../__pycache__/auth.cpython-314.pyc | Bin 0 -> 11685 bytes .../__pycache__/catalog.cpython-314.pyc | Bin 0 -> 18140 bytes .../__pycache__/collisions.cpython-314.pyc | Bin 0 -> 10512 bytes .../__pycache__/dashboard.cpython-314.pyc | Bin 0 -> 3958 bytes .../__pycache__/satellites.cpython-314.pyc | Bin 0 -> 7802 bytes .../__pycache__/weather.cpython-314.pyc | Bin 0 -> 9058 bytes .../app/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 158 bytes backend/app/__pycache__/main.cpython-314.pyc | Bin 0 -> 9166 bytes .../core/__pycache__/__init__.cpython-314.pyc | Bin 0 -> 163 bytes .../core/__pycache__/config.cpython-314.pyc | Bin 0 -> 3081 bytes .../error_handlers.cpython-314.pyc | Bin 0 -> 12106 bytes .../__pycache__/exceptions.cpython-314.pyc | Bin 0 -> 9668 bytes .../__pycache__/scheduler.cpython-314.pyc | Bin 0 -> 9398 bytes .../core/__pycache__/security.cpython-314.pyc | Bin 0 -> 3083 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 163 bytes .../__pycache__/session.cpython-314.pyc | Bin 0 -> 1332 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 161 bytes .../__pycache__/db_models.cpython-314.pyc | Bin 0 -> 44048 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 162 bytes .../collision_engine.cpython-314.pyc | Bin 0 -> 5312 bytes .../__pycache__/orbit_engine.cpython-314.pyc | Bin 0 -> 5290 bytes .../__pycache__/spacetrack.cpython-314.pyc | Bin 0 -> 30450 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 3496 bytes .../__pycache__/base.cpython-314.pyc | Bin 0 -> 5022 bytes .../__pycache__/cache.cpython-314.pyc | Bin 0 -> 6176 bytes .../__pycache__/celestrak.cpython-314.pyc | Bin 0 -> 6875 bytes .../__pycache__/chain.cpython-314.pyc | Bin 0 -> 5963 bytes .../__pycache__/spacetrack.cpython-314.pyc | Bin 0 -> 3050 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 162 bytes .../__pycache__/api_schemas.cpython-314.pyc | Bin 0 -> 18738 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 163 bytes .../__pycache__/risk_service.cpython-314.pyc | Bin 0 -> 7432 bytes .../simulation_service.cpython-314.pyc | Bin 0 -> 5084 bytes .../weather_service.cpython-314.pyc | Bin 0 -> 16871 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 164 bytes .../__pycache__/manager.cpython-314.pyc | Bin 0 -> 4731 bytes frontend/src/components/EarthTwin.tsx | 2 + .../PerformanceOverlay/PerformanceOverlay.tsx | 547 ++++++++++++++++++ .../PerformanceToggleButton.tsx | 27 + .../src/components/layouts/MainLayout.tsx | 5 + frontend/src/hooks/useCesiumPerformance.ts | 52 ++ frontend/src/store/performanceStore.ts | 247 ++++++++ frontend/src/utils/networkInterceptor.ts | 70 +++ frontend/tsconfig.app.json | 21 +- frontend/vite.config.ts | 13 + 54 files changed, 978 insertions(+), 6 deletions(-) create mode 100644 backend/__pycache__/debug_log.cpython-314.pyc create mode 100644 backend/ai/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/ai/__pycache__/openai_service.cpython-314.pyc create mode 100644 backend/ai/__pycache__/workflow.cpython-314.pyc create mode 100644 backend/api/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/api/__pycache__/router.cpython-314.pyc create mode 100644 backend/api/v1/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/agents.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/auth.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/catalog.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/collisions.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/dashboard.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/satellites.cpython-314.pyc create mode 100644 backend/api/v1/endpoints/__pycache__/weather.cpython-314.pyc create mode 100644 backend/app/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/app/__pycache__/main.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/config.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/error_handlers.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/exceptions.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/scheduler.cpython-314.pyc create mode 100644 backend/app/core/__pycache__/security.cpython-314.pyc create mode 100644 backend/database/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/database/__pycache__/session.cpython-314.pyc create mode 100644 backend/models/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/models/__pycache__/db_models.cpython-314.pyc create mode 100644 backend/orbital/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/orbital/__pycache__/collision_engine.cpython-314.pyc create mode 100644 backend/orbital/__pycache__/orbit_engine.cpython-314.pyc create mode 100644 backend/orbital/__pycache__/spacetrack.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/base.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/cache.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/celestrak.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/chain.cpython-314.pyc create mode 100644 backend/orbital/providers/__pycache__/spacetrack.cpython-314.pyc create mode 100644 backend/schemas/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/schemas/__pycache__/api_schemas.cpython-314.pyc create mode 100644 backend/services/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/services/__pycache__/risk_service.cpython-314.pyc create mode 100644 backend/services/__pycache__/simulation_service.cpython-314.pyc create mode 100644 backend/services/__pycache__/weather_service.cpython-314.pyc create mode 100644 backend/websocket/__pycache__/__init__.cpython-314.pyc create mode 100644 backend/websocket/__pycache__/manager.cpython-314.pyc create mode 100644 frontend/src/components/PerformanceOverlay/PerformanceOverlay.tsx create mode 100644 frontend/src/components/PerformanceOverlay/PerformanceToggleButton.tsx create mode 100644 frontend/src/hooks/useCesiumPerformance.ts create mode 100644 frontend/src/store/performanceStore.ts create mode 100644 frontend/src/utils/networkInterceptor.ts diff --git a/backend/__pycache__/debug_log.cpython-314.pyc b/backend/__pycache__/debug_log.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3880fcc68e9fc81772109408c59d3f18fc9c751d GIT binary patch literal 2308 zcmbVNTW=Fb6rR1B_XcpcT6h`o##&q|KOXW5Zqmd?7) zxE*&dnvtTd{NoLz59#u-P}_18-PMV7cPGlVJ6_!byux{(E{2fiT^u}RnGVTbEIJlB zkS1Adrg95v3STT{)SQ*E)qPppE@Cw_bW96t0`(iFX*q_IwM=^&*3ziCuo#%bg&Zbf z{#lY;UB4c=fd>bL^?qPVD28UmqhBDDg1sK}k#I@S1zpr7UDjQ?`=A6Sd7g?ON~OG= zp_1(o>P}~IE@cxIC+a5HDH4;))0T;s5g6c3TO@BdHoI{*J$h;+e$K|kjz@`MW*t>G zOdKD>_MBrC;*;FW_(dZ*2Y$x6xe4$xTv(uiM568*P9z8mYXJUsACMAyfxJTBb4IS& z&)8H>@@Xl>H`hT3alaYEh710FnAUS(R|&=WCL>Yi-KG&`(wl}ZG*Wn6*jqo#s4Qx| z|3{nH&{hy*Vnbg_VftdCt{j6KYB|v%2eIajsrQ(KE=N6^%!4H>te6n>zJ4-X-)ct< zu6er@=RjZr(U#Z0)ZOQ$;W{9Cq0mFf#5}R8Ti0SO(*du)=-^nB9<+ z68Z$4ZbRrQm_vdwApl5@i6deF4N3E;>|G|FW6IpLcE-7Ci&D`k6XlFNy;5-~9raqjQ7}u$b8>PvFA)P=$Au6YHMuxiZyh99r z>z={7H>95lF~YG`0H%bV_*-s_Tpzg+xgIHpD}Jr&A6xN{eXrlXuzcb6?DFg{6O{vF zrLjl;wwuE%-9z^}h8`-dk3208+B?2dZYir>qd)BVdGOBQPvJ_}Xr+DhnrlsIyU}vJ zm!!?HACR6Ihb;8$hQB`Ga4AqL>WTkazjg$!vSx<1eC!ZxR&Xu!tK zY5s6Hm{9La)@P(r4Iu@DrtlS{L&D@%~FNb&yX?{RsB4s5L3F=EE(nZ$t;0a@O zP2OT;7ZB=p77ENP4tOsBdI}_g!CidAj1;U-JCvhIjHs7gRjg_cu+S!Y=>- literal 0 HcmV?d00001 diff --git a/backend/ai/__pycache__/__init__.cpython-314.pyc b/backend/ai/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4b7b5210ee78e24a97bf5e990608d019ec97d1f GIT binary patch literal 157 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 KGhicK*8c+e@Kiwn literal 0 HcmV?d00001 diff --git a/backend/api/__pycache__/__init__.cpython-314.pyc b/backend/api/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd90041c28dd99bbf2fe173e96ef5986f3b0ad14 GIT binary patch literal 158 zcmdPq@YUEVrw1QChqBq zt#@Si;z(EQa7T6&_jScKIcS1HiFw{QfC>}hG;zPeB z5LbjTm9}xA#3_sGB1lSnRhS;(G?#NZ^@QmZ!1LTf-V+*Gp?U5JBTKy1;Qr@`T<G@Y`KwikUmKn4>JS zOq>@qpUse>n_>QuC5vmqgz#yj4wHAG54tx1rh;~Q2R`tRPd3-8@y`qO9=jg2*$5!_ z;(bDYMnGiIWpYj85O~Q~7n6{_Sa9KxSr9(6kTi1POqV7sjUkQvAvG#Dgovn{cXFjH zecFt*=BTbv?#uEQ>sMznNe*9oS;&@h^g25Qp#t3tf!$RVMfr|S{6I_FXlV;A{frF0 zU#OTn$lOLFTWI9tnT^DHqI#=t&(*E@IvV+k7QXF+6Wg)GRxDAyTRUEh`rzX(!Lu6| z)-P1;+HlSEd9!U^d^>slQE literal 0 HcmV?d00001 diff --git a/backend/api/v1/__pycache__/__init__.cpython-314.pyc b/backend/api/v1/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b90c77a4994000df5b6f5c662fdb66be3153a6e GIT binary patch literal 161 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 literal 0 HcmV?d00001 diff --git a/backend/api/v1/endpoints/__pycache__/catalog.cpython-314.pyc b/backend/api/v1/endpoints/__pycache__/catalog.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c5263d1b44c69ee720b038d4801b3fd0281555d GIT binary patch literal 18140 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 literal 0 HcmV?d00001 diff --git a/backend/api/v1/endpoints/__pycache__/weather.cpython-314.pyc b/backend/api/v1/endpoints/__pycache__/weather.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bc5d3c1137fcd839ef3f0354448ccec2d61c9cb GIT binary patch literal 9058 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 b>y{XQ0px5$-rHn=!0?M8PB4F9jY#}|bwocq literal 0 HcmV?d00001 diff --git a/backend/app/__pycache__/__init__.cpython-314.pyc b/backend/app/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b31f9f6a666fde979e0febd80cd4af00633261e GIT binary patch literal 158 zcmdPq4HS1S*S<*DI*J g#bJ}1pHiBWYFESxGz(-~F^KVrnURsPh#ANN01*i#EdT%j literal 0 HcmV?d00001 diff --git a/backend/app/__pycache__/main.cpython-314.pyc b/backend/app/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bf6dd624a6b73ae98204144b869aff547a0d73f GIT binary patch literal 9166 zcmb_BS#TRic0HIGU~m!yNJ6~Cp~MR!1?n^@inL7eHYpMwft0nT41>Us8VSU}GXovS zZWV3smZ@DQl44hhaye^OK9beORrXI3eWWV3b$CBPkul*}sifUaWy@coELH5-{>bYY z3_ww`c9Tk@(DSDI_3QV#d*0Q}E~gDa*?;WJ31$;Qf50zBDbE29en}!Uhh9b;F@y#Q zg%~s`rW$mNs6~5nC^fj(G(;+-_M{X_d(sLGPjbjSXi+SKjKU0B6|06z4cP|mid}>0 zA;+LoacZ!6$Te7`6lt(!$URuB6l*XuKwco;F=uFwwhWkTy2|0XHTg* zRs=Ft88X5;R0a1xF2ylBNYMFsdttx1E!@@sq5bRHXdP3dcHr?*$BLi49$bB!Y2C=~ z$47DvTqDfVWZ-drU;e}?wZQw6#%$X#rg`0%Eih)_3G=iVE5dE}AuixUaksJUN*&kg zLrOi2JoJQ-JMtrUuA8Uv2e5bLv3IY-ZaU`Y_O!o^4w%)xe_5`|bK0bLDjcS;cx;;fjG zbJ!s%GCeIs*+bfNvwT8fgYk$mElRU#dS_rqU`N{7Dkk{^a2Gfsi-|Pd8WW>(+T03L zic#SLXaJR-6=T4WJ;uvXNzg<~+Yd%!EXTi^;$=m+h_M{wiAYL0E=oe0j|uNUQ`$Nz zDr~=)O2mZA7=`J9{fG-uMff}1?VgClfp@%AthNfmf8pk`@uaLsd}NkA%uA;PkWBA< zh&xM%60n4cNL=gWCB4sYQE5d`V6BT`=T*xoSahJO+HJdN2AmXGE!JP7FalOjh{RWK7C9A-@-lE`nS}=&mQ*d^ z4fh2{`Z%_Gh@FbSSxv-PI1ExMk$_XkuD$>6+!Z7>V50CxYFkgoj@s6LZB*;!nbpVD`v#dmv zl&o5G`e$KD^yA<+Nmjs69YJpF`M#b|c(nUapC+}&k<_Gh>Z8KZ<9zg_)Cx^)@Ruv0 zG9-C%@$5(be|d4Wi(BgAGWNq6>aZ>n*?L-z!eK-Bj|*m169;oPhbGX=ruLtLih{N( z^a635;D~}?tpo^R`Vx;(t*7}ZSyL*0Lc)szC&7%rzK9}Y;#+dehQpCWLIipFa9G0H zwhR8U7pgh*H7X|>?&InMtM<^C)cAvbt>EQ&1O5;Cfuk*`21XV5dD24Hf((ZG9e|^E z0>+&o=ExD+2BRzCH-UmVQ>IU$*M=e3I3`IUtC_P*P!6=q)SBnr;Bey#)u7=I5CX0l%=L_Ra)$pATJoId+Wr--=N^A~8_NG9 z_trn3`#xeAR?>p{h!KwkjeHCLlkj)3XtKm$(2Qd6eO18;T6g-&jNZbt7KQSsuLf1a z`P+&n!Qs{mQ0c%_lJsPS(H5*Wpu&^0g=oC?#)|*%XHC)vXgoIua~+@sLO}b;Y9tVQ zNrCX&M?nQez*K=5u|&a=C%^<+< z@xn`totDJe0%p26?{Tm^b_Y6Gf02YwE=;ZaAF4y*qr7m64=el`MWwK@23J9hfCVCF zctvW**j5=#f{sDQQnN`}+6T=Nc6n7>Ebq*y);_~WQO(#V;w6>VCia^ptnpO}%yL8% zPr{zPgq5dC@G}0&8(3U)&E?4}xG>C|6orH$VW+Ya{_+&4xGzB${kyIBeCy9!7yQe% zx>c$!L)ERBd~`$B<;j+BSuJl}DsNpb-?{4DIp2G`tm4v%zdo^8vRu|MKaef0y7bb; zmliiImu{Qy&z4nRI(hNrqJO!pWq#mZQRzbEN>Sa1zT0l!s=M}j+%e2l~s& zn_e@t-z=x0<>qFL^N5AQ`W|#1_292S?-s)6B!tgN2%oXz!%?79&G58vOe>Wn!3IH4 zU9eM0YJv*llf0pDRzm~0(Kwwl?LaA0Kg{>)IA?3gYY>D^nueVSSlHGAg>r-}5P~~H3}{KpSR>;J z+A?p~g4og6P&ik}Qm~Q35Ex@AIXnLCft|z!_6_7q!TzO8Nm@W)3-Y!fv^e+=*cO0( z2P{LkYS?6wNm?Gm-=b-K`#Qc#^?BCUT+z|LD9n#(C+!F9!@oc|sG!AJ)Qs}G0sB{l z1mLt9IKn^7ldkRk?sRW=EXLyCWxK>fcm%=LD=F+YzrsevcxpBwv%)kRNw8uZTzpmr zpK>;W9q(B|ma*flKtllHO$$T}NeC9f+s8`UI-QEg!->c&pLR@ib3KFIT+^<-d;JhW zz+B;^B%Tt0V7e~fxx>$nj)mCK@sSa>x39l@d?ds+#>HqP-l*DUL~$n0hry&kG0`AXw!62N?HL;xKQzj!1e@LrqJzAK zBpObjUd{;~NZ0m=sd$V9=ah}WB5mmJVC!YSUA4)t#v^gOEN7)5*e&UKs8l; zmRXckBC0a{8Hl@Kj1?rLg%_NeIbhY)*C4rP;5X0V@Z)CF2AEmMpC8 zi^1{~3^)pZc^iE7bLfv$)0!DsOD?>eWqg_P16iizLPD$7thCEK*T3eV=%O!5Di`|S z9-Fsj8+WWWb}lu7fmmtmTlV%ZHTEsmUYNc#bYTdJ+oIyJWv;&s23@ zjeYL!xo<%g{;x@7+jMU8e}7esN(P8;G~+|iMJnT3BjNe|12Zb(h;JTxP)RiyE0_dR zab0wQ75%t()jfL4J^CG>LdUmX+kw{k&GVbTm1D4G?^O1*pbv|C8mUi^3BXTEZ9UJL zKdCcA{b^BI?{50jhTXlC`C2&z@U=!7aIUr3d-jvpItZw*Q}*5+*6V=TY`tD@2Au27 zG;Y~JVtBW`kFr8Q2;YE`N=9MAe8Cpt4fsF!571JN#X``wv2g2r(MmnBYZ@ezXpjQy zL}TlurEkqia)JorSbakY;*`UNG69L+LV|UjH~gge^ST*ya$XY`>|PC;0BvsAizKD- zNJARgmZS&mNi+0QK}eQuEEBY$>w`Gz-B9xMEEATzmfVK5Bv5;Tw>?3FMS*ySV~hl( zL0y}_W&?S6HD>rh{>%o2r}D>4Fn<<*X5i1t+5Rv5{fYg_r@=KzYM&;lwl^-El26)d zux;M{H0(byZ;`P+dTNtJAu9OP*$o>Db{K4_Uce4RWB}2Wb=_<@Y}ru2Td@9MG-YhS zx-p!yz#Uj&Q__0CIx?p}*W2JnIH~)yIB{>}#GtPyc=1ey8MIifIK6EM(p!+76Vlio z2Sr(#k@&+ejIcP4nTp7K&T0n&0Y52Wt*%lBp;>hthU^-|cq7=zj^ae!l#qy}aO&&# z@BM5&C7=`M!7xH*5_<@(EF>9)X@QSn%kLPIW+20s){>bj#ffn~T`~|Cr*NWDOWgK| zvyk+Sr7K4uQJ8Bh@G+VWD`^PfMSjgLV9ur^4*Z3%y+Vy zzJ>*RgrUR6SiL*Xkl&{{2GS9rsJe}P49NO^X?rN~&?il62wIL>4#cLyI;1j?ppD>R zs!iXsFr;!NY}BRWxDxO%8WPyjS(Tm^Bw2x+dn~4!qp(wgqSDd02tL6;cn-gPBXi~)OQzV=y17YFn!%Y?ShjvlACr-BprrdJTCGRf zHn>a3**fXRfN%<9o&c{zi>@i#o5QaSXWX4DRM(oRnjRzWG6$BJ1Aky@vW(-r^PF>) zVV4+oaXQ1WS|I5Z77tI%cobeyX96GV$JGtVH<3{U6>pS*)-qK6$ z7uy$l-+p%0+qmRyT=x3sd+&OE#xP~EBD3%CiZ8U{9?y@u?eLr*J~y1%)Vbp5y6146 zA3Qhs*7IvNn(n&8?8q|3t4!q*Q@O%arHNGy1uV@0HGUoCP2E;;OT28Z?u!peq+DA zzl*$S-rnC%-rP+?@6C1^w{(#Je}?P>0rE5ZrU5_s*;X@d@zb~^fP3MJ>3B+siKi1$ zm_J`AfZl-rgIA!^ot&KO0(a6$2ydRAzBLR=A<6kjxCS>6hAGk1D=C>^8tEidqkc-l zr%ylgj12b(+GnMGB^W_VYv+7xJ3h8K_N6)KT*L_AWN7DhjiP9$vAGpXOvUF+mEnUr zRmOqW-TA#-1?aC60ph1+Rp?z)fOyZ|O#VF?Abw3Wlm7rul?jLOwM96bcHnOl!yWkU z%mTMt_$NFE&I#P~QOCh5s8mvv6x9rI9;D9v&8jtjF(_f*Ou{RxT2gTBBuWXXQ+qRR z4K34fR|q#{5{)r&DooF;`Eu=n+5F`oRuQs06b7g8-MBV7##Q(vAY&HXNgd!zu~)R~xTym`+fcU{Icw zF2e}WbVzQ1LkMSrAnu|)Um)KKD*r8V{TA7w{v+DIg!bP-yY8U6JLte2RC@>2-$C0U zUL%-QMD}Qi|?PtM08!?yZ@ky2ZYXb6d8!e6_f4skkoVu3wzUxLWQp_W79$ zKUtjp=vca}XkjtK8VWF|~& zZbVS}fD0Xq$5$KnEj8@RAkPZwfYmb5Z~9*I&3i8lUg((fEfbsXn+W0%@qk38nl%lC zkTr)?zRbN{xw+&jhNC lkJl@xyv1RYo1apelWJGQ3N#R8Wig2HiJ6g+v4|PS0s#3#CpQ29 literal 0 HcmV?d00001 diff --git a/backend/app/core/__pycache__/config.cpython-314.pyc b/backend/app/core/__pycache__/config.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..432ef3a41485b7332745681edf284f23a88306de GIT binary patch literal 3081 zcmaJ@&2JmW6`v)S6eUxnsBhV_M4Pc?+7d~9e>iqcksF&9MbfjB5~H%WD{@I~O_B8M zP^pC;h_pxpw1=ih(&o?tITUDs{sBc#?I~BWEf95!qAhwTaucfnPH%m0MX8Kycz}KL zdvD%+ypMUaH$2^41lr9<-}!b7&_9tmZH^W=P64Qs$Ak(Aa!=sGIAUAEcF)f3_e3sQ zyglLIjsbFsP;rP*$0K3RelnjsA2~0Pxz=L5gW3Zm===itpkTUJRYQ|C&h>K92--|B zsT=z&^oVeQ5^j6!QffI6(+PH(-xs&$7UVYp} zMczpryo);dIoiS9)Wy5u^UzN2rCq!Sun)dodXD#Pl39Vehe*jqyMgxbe#;^cSmd3v zo$SsBEUg~Y;)B%3hc*cxrY^9m-*latvfsHTWs(y0M-%%UVYRA<_omG|k>&i%ycSuW zE2!#1PMw*bU&t*@FHJ8jY%i+I^9!@v%d>^W$ntVwp%5v|9+X-wEI`O?Utm~GC)0S1 zm4h~hIcD17G?0wWLWS+B-0W7ATuC*Iyq@EVV&II+b@O&?WoyIGn6Z^%YFX!gs+P5_ zxMn=zmFm`-6`-wcuum)Jw;(=SxeC*OlnZ(>RDEW;6-6zVD_rH8qL>4xnGJ3erir_WlU`c3Xnfz>~HGmXIN&(zO~zb}5Kf6)13 z;inz%cYM?d!iG0<;L7~x58jM{1^5?3QJ?tRejvG)FRku0#gE6VJiH`(g;Zmi?5Zf*qzyQOfErCR&%G>{jx%5@JZfr!}?I8u2PhL1zXp>1C~4 zfngWS2T@5gJvk`TilLPao$Gs=IRMuH5?9YDDhHo#*SKaV3LA%WvOXl1N1Z1JVR-y` z_zX9YcgbPj$a}8Ck&8d_oakcyeCyp_O%_Nk~X+_RbvrCp@RLWw_G%rMBF-ew{Yu1P-k_3v@1>(`(Ap&)%|=(FvZQR#q}fTOctWw* zLzbv@rgmW_D$8%CY23UZN3&8gnaD~C7_ga$OB6r?ZhZEk*&CCR5||KOQ_lAEq%%?~ zno!U{w5aE9I(;`ODY0ZiN@bNq-0V$7<){)*r`FE2f_B!8$G1b}U|7VU#(SV+ULdVf zZe=U;p|lr>6G#39_)~zE?;Sg zGfgitJtT0oAM!6aJ?a3YeoeWB?U4x{;~1l zZbQ7@T(?A2u;yI*w~Xw`EyTO4e+%xS{<~j+mqwT$1nd%$%SZx9f=J*&AnXPbDC2}p zA;A=3AtYfW_!cuPk8B3XEE0S!7?uIHfMgK~K5Y!|zv*jx_6oIf4yp-eIm*x8Uicj- zDN0Z{i5*tQJ13#*z-5qFp?eJe-p#9&UBrrzffsiub#4tmCT4akvQ5QM*y>whDc{z5!o aI;VxP_rCijfv&kD5O@C%SKe`1(f=>A$LHGs literal 0 HcmV?d00001 diff --git a/backend/app/core/__pycache__/error_handlers.cpython-314.pyc b/backend/app/core/__pycache__/error_handlers.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3391163db3b211c5eff250676a01560a36baa9c4 GIT binary patch literal 12106 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 AssI20 literal 0 HcmV?d00001 diff --git a/backend/app/core/__pycache__/exceptions.cpython-314.pyc b/backend/app/core/__pycache__/exceptions.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a419c2bc3a8d57bef45fbdb2fa3a005d07b7a8d GIT binary patch literal 9668 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 QKIz)}-Ph=LO=Kbe3jnvFO8@`> literal 0 HcmV?d00001 diff --git a/backend/app/core/__pycache__/scheduler.cpython-314.pyc b/backend/app/core/__pycache__/scheduler.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c077931d3af76cd31693a12e6a726ea1abbf533 GIT binary patch literal 9398 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 literal 0 HcmV?d00001 diff --git a/backend/database/__pycache__/__init__.cpython-314.pyc b/backend/database/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fc528c36b5a50a58c20981540f5771ece6d3a0e GIT binary patch literal 163 zcmdPqV~OO)n;6YOEaH17(1Gq|grXjWuJ{RAyD{=9;x?D|^*Z&T2swhM2WB zwdyLjidyD*)N(gwC+<#W-`??dPW#RkdxR;kWuHfW!7sEtf9i@gMyKZypzayu7hBHd z30m^KGf2(&(~GE9${vEPx51_A2)6m|I^JYbMm)if2KO+m8*$KOy*_@We&D_Ne}=aq z_jx+=yd*lP4X%vW#EGX}_m+9Pp6gE|s{=Sb_!X@qHH7y_%8F1cFh; z$S9hcm6zm*WsmMLkyln&5K{r(_t`R)ET0-D0gk$oDHSChxdq2&QqE(N3Pw_Z^5SqQ zqYqhL>d^xN_lez@e{da4J(?*V@Ln%cc~R@TaVERD-HwtlI4LAT#&~hG0LlQhkTRf8 z%$)^g`G&H66RKq8wvOu?bgSm%E+Gl+F+y^WkRA`SSnDN1Zf7(e?^Focjf7M&BtPM> zJx_>KKrkRwDG{|Z#Uvr30(Tzi0P<^Zzuw%IOvvVjph={#PZQQ$VRBFLw0VQ2F%!); z4fdc)&5$bEhJc#mG}hC;D1(=w&^)9A;$!r{cE$Vz{}*fBw~O!{?Sr_0s=&NC$ppF# zm>wxQhu%X6Xkb)f-Wd1qXm2DZmQ{FLiwL8w|FDYCRqHtv8LwKAQ8RAU%)B%%^{_K@ zCy2RZP1bH_!;GY5qyWpwJDYCQx#6|{0Lzh6 AF8}}l literal 0 HcmV?d00001 diff --git a/backend/models/__pycache__/__init__.cpython-314.pyc b/backend/models/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..67c20eb0b21042e2f562bea5a3ed6705af7739f1 GIT binary patch literal 161 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 literal 0 HcmV?d00001 diff --git a/backend/orbital/__pycache__/collision_engine.cpython-314.pyc b/backend/orbital/__pycache__/collision_engine.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d05f0a388dee05469d776faaf58c2aec6af8b67 GIT binary patch literal 5312 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 literal 0 HcmV?d00001 diff --git a/backend/orbital/__pycache__/orbit_engine.cpython-314.pyc b/backend/orbital/__pycache__/orbit_engine.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2cac840922e40bebfd2ec927dfe5521158e4fa9c GIT binary patch literal 5290 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 literal 0 HcmV?d00001 diff --git a/backend/orbital/providers/__pycache__/__init__.cpython-314.pyc b/backend/orbital/providers/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1cab4e499f668a811dd5b9e27acd46971622254 GIT binary patch literal 3496 zcmai0-A^0Y6~8kc+hb!eI3^In#+R@_tPKuigF=C&O$m`LSa1f`ZB=K@FdjP-%*^!8 zH3>-Fx>cnneTgbn>Q;T(N>#h5`jCfeyN|8<514`$IwIPt-9GFalvL@y_1rOIvw@~p zn)@~9o^$T`_?`PkOLGuG8^815-me1){evys<~f=i{uU+`Bq564MG}|dCt1}l< zZOI6Y#Lyis-S4W=7<-TuxW?7iG@3wCXr80(Qm_L_O&zGzO3zAMFN!zIA`k`NE|;h_ zXc=XqsMwOJS}JK&#d(>^*d)f9mRE^|FKL!lR&np}NIVe?ru3p}QO(e?Zs3vuD}wW? zsp@$P!={WaO)r*ITsAFARCzUlmzK*~DW7v0=9Ig#rY~XHveeb(l4@akR}I#bu&}1H zB{m3%#zk#S)$#H=Ub0k5fv=TVxUo1rJDXaZ&ZVXKOzP&e1e4Uflv@1O(q&MjAhSE^ zMNEycU=TyIOj%I}DUp>GJUEFJ6-2_SwvZK&Js0O~&kcRu7G^bz#yMMb#l<~#$Bj}c z?b_m&td-RKG$97Do88^ovVFm|-%T)|`wy5!W(`FyO)*KwcH5?y9*dyIvCTTmDQ2Q4 zaCU(y4^&E>%=XBpsW2BGxfuyCXMuP)1YHGX(7XKT4-v}4UN^eU-Q^@M3x8@&;4fT@4x{o zn0ZhXBbMWLcwfDQmj+%;s_$9LnVq=DHoq|#gmp*gy zOh$0!n%<1JaEXOgmbu4i=?-_+7$x8yLXtOo&fUv!E)UrEUG=)F7@8@9Tt0o?rEH8D zZ%^G{$LPPuTzmn})b+pol18sJc(^mk-{6pC$>OvHyLhka3P{xP(otF`8bX&6L8W$?TF)%b zBI}SP^8|N1BO5;GMNhan(gYN7j`yvj@dVVdy$ZwaV~P+K(u7@O z7(UAj9SC^R?r^rb%NrNI?E<=W2fw4Q=*E4$UQF>oc?3=jFdx36lHtEseu}lCZh&(W zc!seU!#0=LC?042Dh7bC`LY6zh9X_IWGJ(B9ma7%hB^-=lEE_6NhijRDrYUk5*ufZ z3v#N_7~T+^`dPS=Ka7#QPtH4CYiJc{*U`z!W100Koa^Kl&TKT{we

a~5p(B`Y8_CqK!R7vec zyLY0ao6*so=;UT}@?-0h=+r}TH{9{~y+`kDg=0J6zRhsoR(Rl{@M-wWUqjK|$oZcQ z{$%h2VJp)2(7)Rh`SDlxLXpRFkLGqGLrxowBX(|zoj>pR#ko(!-h&R*ev^CAg+lFnV$0*uqtHIjH4npqK=8SU z&R(is8ry0g-+pUu>#cNE`bIVS&4+={gy4^6zCTlq^nFnH&B`xVHd$~C&?V3aw6G2 zf68C?)$3f~)bs4g=|rL3ee!$*+MSEq0SNP1C&&p@U&L+pIGc^9UKo0+>_eZt@Jr&=-gg_Ht@xSPVr=TP1zr-R$$p8QV literal 0 HcmV?d00001 diff --git a/backend/orbital/providers/__pycache__/base.cpython-314.pyc b/backend/orbital/providers/__pycache__/base.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c808296d97ffb9feee015ef23d82f2f8fb8aa9c8 GIT binary patch literal 5022 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 literal 0 HcmV?d00001 diff --git a/backend/orbital/providers/__pycache__/cache.cpython-314.pyc b/backend/orbital/providers/__pycache__/cache.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a134f56c9df2e48654ae9d0d2b33205b1031ccf GIT binary patch literal 6176 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 ETal>K8vp k*DI*J#bJ}1pHiBWYFESxG!JBBF^KVrnURsPh#ANN0LvdH)Bpeg literal 0 HcmV?d00001 diff --git a/backend/schemas/__pycache__/api_schemas.cpython-314.pyc b/backend/schemas/__pycache__/api_schemas.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c634b80bc96d2d6d5c220db9859e2d7d7b22313 GIT binary patch literal 18738 zcmcIsTWlQHd7j;!y>NHAykEu3kd!P_mMF`TQ(LwSE21pYmMD2i`I3kl4tIy-SbJe+ zhLXtUva(ahNFzW>BUCDqKzc}PJs5C+qG%q9qHUTK%|pe2N~LL&)Gmr54bYgzTF=+Iok!foU6{ZFFcGnYIbqW{0+kXf?t}IzhqjMt z_d|QYq1~Q*x{vGs)Guhs`vXSRrzK53kx$DRBXTq=DVb4SHG(fGawgp$GQ!8?oUAG- zBRn=gE59bGM&#tIuHH+jpdaUSUy{@PXTy4+nagvpar=EqUkjIs=zP+z2Vdj#P%?lA<&D}?(zKG3=&qxw z896JZ2M{Nc+G&-)7K>8$^mFLPWP+79kj-;8fBH|qN=_nbfJdkk%?W544# z;5g>#821hSf}YvnXQgS`;5B7IHiCLy$GC%}(lFY9lGFSBMrbOpW+mOI7R6LX(zG<@ zLKHQ+ytiO{`pCgEr!`sC&YV)EoT3X!DJP#9l(lnue)h~O@@z&{&rC|Gb8;?yMq}gC z&PcP0XdMsC&Kq%2lyW)TT$e@B=q#U)0q1EV7i}NJufM{*&E1byUm9Kxa6aEh>?nw% zjtNNna z2C3HAf=2X^1V;1~?#rCoVW07a*fTiof>^Z)v7`^s^65eR3o*F)8Qj9jfF1$ds**ki zx7guR!%112&F3_E#09k=I%ZJ&CJriunh&tEq2>ql0#4q=IU9O(jxRgMpa?yk3*w&- z|3dry&Q{K2^cJ+h8&d1=h(=hOOQmE@Gs0O&24onhWJG~r%c6`(R@Nob_J<9AGM~>F z0Zmuc2(DMFNw9T@(GhvHe!tp6$85Dsh?~nr?!;(y%$*CB)RXo)go(=Px=SZaR94qs zI=*b7GS+Z)-{mJ3`3@VJb#04n{da43E%N)838BkTuBPRxe&fikhU-JuzIZqB*do8@ zK@4Y1OmyM~{ZX|WAF4p2heR)lJtPK5>?P4hVmpZ)Bxu^4*y|@H3HIF7EuOo;eG=l@ zItzpM_UtRXe6N2`VdxRv`yC6B)ZT?i!VCR9d;`LT$fgxUjtAkbfrBLfdo-Q+(?d)b zOokk~FheZ-HwZG4jObNBY&01JVyleUQB}>WgIGr;v+(2zDK!I=+@s1^*2#=4q-8Co zDvY&-{FHEEM$&}~at=bx=cWe+7Iu#6tf5TZ2ud2~lqp44cMI~s^nfsOa!h>bgOo7dm+_ZphGHGqMa9+wNX_9j?zRk@7 zVzQJKz>DXVl>BkwPhEyq;`C4B29+8b8A_QsFvG(X1x$0`Q*d)eIL#(XgO+I0M0y3# zc1*Gh+D+(0BNQ=cgIr^YMgCPA!inaqsmtTbTp$=?BQHjgc zXGv^;*aOst1bZCvvJA2J`GX(THeE^G*?DTI_SM4R{m$Ndk@&sF@nSI06)GHiL}q0J zm@fAQ@HcSA1>o+@1DJt2oQya?9kFy(4qcU{i#l{s27Uyf0DmkQ*W&tL8qI-X$rF$DJ8Qjw7eh*sf+@Hil(F- zZYJTvQaAY=dYXsUbT+~TMW4yf=`b3&RpDGNe}PQs!i%47s~y_}ZQ1JRcj zdJbnUNb{O7C1o^OU_Dxcv%6KF#*_>!JTWwvm2xYrAYa6Dno-qmJol6==y@TnXwc4& zEOeie^ci9c8kH)44rZ(47|byRl9AI3&m76Cs+?g_87Sj90r$^4XOms|#-5~k?VjO5 zVFsg@)rIcUV@Cvv5m^g!AudRoFsmZYNXcn$CiprAQ7IERU9Q8$2FQh$F@)`6z0NUb zifiaxxoZwtjyBAgMSTKKr;^bbK{g-?8}%6@YR#Oeq>U;XGJ>J(Y=2OFlG-!&VRp-$ zZ5+Dwy=%J*>Mjx@m#0lam?TuoCF+TU>d0x=?;@1O5cdg4pAEtY*=sNfRWjpSl3IknXIYT?-ZhSuv1cXqwL z)bK{(*hlT%H~BkHjV`s1f$D@Ept?KlW5s~KCscSD%)DdQ``hn4Kfbj6Y+?8jjm2g@ zx|fUjELY5}&R6zrn6Fe(xPLs42+JGFPvK^ph4P9M3nBqhkgLri)3}gV(|`=QONOTP z;nQ4tE*EqNh8jiO3_AB?4VO-up;-b!+5-yBR#tpO?V@{?hf7CENrFLoCw*fvI;mXK zN~oZ}heHr9hw`*Jt>ZkRA>Y~mP5MjG5R2Clvzrl6z>0u)D*~dJJ!nK}Xh%S=x;%o! zESi-riWxaKt0L zJFZ#ed+flxx$8#i+V~>hUmlNphB+objNq>7NfM_>^pRLQD1U`clVDFw?Mzk(al($9 zV;`oLx(^nH?{)SRj@{eQUpVQC&FL~15=#cYDqDPhCA6VJ1pb-XqKUk4lDD!&KSN&K zv3wqJ#^E`A#&UI14U*XCDQP~Fhqu=#(4L%UzHSQfwvND9kfwAvbn{AKn8n-ryfA78 z!*)!fAs!i}*1KfH1+uye!Gds-ZX`T4@T4#yvD8k~^Qat52#>SRJ!Y^WOl9&H`UM2@ zQZ727q)UxX=44?^nicj6DFo~k=Svy0@qjP}PjA&B1cXbJ0cZ$56qF?JHL5TVeID5X zo_|_WatH{l5lqUcfWV!rbDB;O3k5as=2Hk11j;N2qV7Dp6y7JXD(jh{hq;wXI3DN9 z!u_~&hKz_k4Y&QK#SmAmZ!uk~*4AY>&!Yp4$c`Bf+-$?P8~d-pnLcK_S|qF5N8Xc;yk=Vc-MyU-6_8g@gb=e;ZMn0+K`gRtM-|Ol{j!O5o9UZmz zwu2XO#>L1(4m+T#3G9Hf)V($nqr%K`(U60Q!$zD~#*W5Ej9?~@nANBO^s|l{(3m$Z z|A0p0tEhyxnYzkav>dQ2CcqMpV;0omnCO}-3lziT^VT>QvCak2M-?4|8KP#&=~vPw zwG-7DQRzI)K^5oJj8QGYl0}#anUrc&DVivybOokXB19t!D}_r-W9IrpDp9;i`z&5$ zzLo`&8%K_9&Q)=Nm|Twb4ur}6k=UhUruS!YfK`~W*}udHB``6r4H?#M@~we<9K|Oh6cMR3_z& zh_b_!GQE+xe1=@8CCjsxvY3OY z#;8{1DWr-srjIb9%#VmVs{L%X41Nv<4W49hBjByine}W?E{M@>G^$~k5UX!m z&{YVbR^CfZ>6&Y)KfAKjR9ki|H`tP5R#fZEx^fkpnL!Z18cY8F>J1qS89W7)D%D}v zU&ZFqv$|IDv$#;}gD_VT$>WjBhZgy6b2X#&W-i%PmNfKNd9E3Yc=PCNqlL>-8xrjH z#DdIn?``WY9KF}xg|#CD*=9-&U29j$IsO%ATx&P6vUaTK2jyv1mNixeS_nN!WuAS8|6Fz2||3RnQ#WZ@-)dGfUX05+ZN1rG01rlE*@g))k5|>E4P2wFAUnWsWcB@iD5)=5R zQ3s6WtoY=9Ti12%!>XmWgN0Y_qskTduyv{ZVBz@vj-H!+A0Alhc&0FNzrN*q;Qh9x z`u!+=G5xbk9fzQ2?c0{xpF?}rPkgEE5c+9syWaQyvrCOn7hby8u^nRP?!rkkccv+^ z8I>N&#i*3O(ci-tAzX|)YceX^cYv4qN&f*W&(?XyT4oLnMzsPVdVdu%8b)VPF+Vx^ zmYmY<)WLN_unqfr*p3g!-ht2S0T3eFFJSEzX!q!9;0Oy4$5hIZMs9~Zm-h_LnY9OM zWdR+3ai3deh_soT-o|o%swMmuL|*wpP)a+LdS$ z?aFDYM4LEQ9txId)Z<)f~#sDD7G)_n&pctOCiH9?$37uugdn9Q}3@;G-$G=XMX z>~Pd(u@0NK%(E78#T%~<-HmlF@|`w&)wkc+ajly&A}i@ZW#H*+d#PGxRpgsGZ=Ak% za*^+|nGAe&`Z5&T%1Q+zx4k1?0^LS|(|gp01daG%DR6q!>I-%+a}bY+8*MYEhAyjH z+swbTB{Q!pxsy#9`Max(ymeJaZWCAkkOuCyW0g(VALGQkbn-HZ4ialy?`t@{1?!~% z#I|0tP&=q8RU3%&92W8pJq5W5qc#t>&Fn(T(5@T!eMAr`S1 z*XpTqngY2k?L~-kQ@{qHS8N~Nyp7U#D>G|>FK)BVpaJ_Yc9hk7N7S#=qfvz9+);6j zlqBeu4lE{0SR_-iu}BRo#Nt=CgvG?5JgF+$rhs^L6%d=;y;O?An;6|y{Hq9s=h1L0 zCWZdB6_I1#KZPv_Nnqd~>y5>-s8c}){F_-!KdxP}nFP5VzaPX4VN>^2FLOL^)_(<%IU$TRXl54u0H5?g|H6TZuM--f!b> zDhax``b`pyJV;d`POohVr`wL?kr?pS>#dj5E{HTWqStb}NZ{RlkIm*asH*kYHbMDQv(4twdTh$GVj7e4 ztr~6zt}lf$tW0M`G1$EKiXFh1WnA}rjx~J2pWzu*63i+55q)FUM5+p{={sAprn)2+ zW;BJDRhzP*4_4Vw>#B|_wVc1e7(bv9S7JGYn+h$*Sp;@kjxz!wYq9eMvIJ&1h(@eU zER5&lDVuPpa1oJcA2Q|(uOcXk!Yjj+Y_w?fMv&0%j^x<-j&e$yWWi#!)}6Lt(ke#&s8iuLW!qrUYygb1WzH4q5&jcT`Ex{T(4o zZ7F0SGq6JzYUl#59}4dY^e>o>nxkZ7Rz_CYx$y3qZqDE6;ONrIk0-ARHm z7pYu}zjp5Qy)7|Utvwr?BChYRBCeM5ek$dyf5ZsCPvfnGwA{AQIH6gMLWmT8p*m=SN$jpZb*{Zi;|@yo!L@R`*mX1^oo0{4ouD9SREK zd;tF*ZZYX#JNzhhUIiOFdFBaJ_(uRMYgM*o#HKIW7UGdoo2Ai3JggB{Wo<5_Be$UK zA>`NX?H(+ED&f%FxU_LR)$K9KP9FLZ?x3B9DCZ$(1+)=c{z~|?=?F>mfm?xFqc4k~3mo8JhaIJQCSwWG;~Cj)aCbnQ5Btt=sm1R$rg zT_Q%r&IXL|IZ?@_<%{a4@We(;m617MyCCgea+WS5H1Lf<+~YaHP8bs*Xk!rNFs`LN z%ywEpS*=yPT-l6d#epc)_gzEfzRS*g>#4lCDi-vtX#NWgg9JT>`d1`aEJ&&fQT*|i zP+WZ&dpI`tqU@Ke;M?fxq*C1eC&ujAkGOT34M zze6As;j-Tw;gfgA8EeomMG#)ankb6xpkcQC1iPK7I4tTCHWeCG*f^@?bJ&e-)TONn z7wupAu8uGmC9i}T8TO)77s58{k#N5gMSqz`a9@3E`F-0x@ZYQUZ_Yrmhl ztD%-nQF3lm^?Gb)2w_eY8ZG7hSQu9V*cM@JYFPV?=Wj3|YPcTIOOeL6 z(fGFnx>qz|GdMAIiNVPT%HVG2^%P_$8_hu}1`kmd@tORzV*iGq+cpFScN_aS`^????X<1Q@&>hyi;ewvYY>U=eNg5T*4TGR|K|+zZFnCI zaXX0}B+6ge{+g5|*eIw_icw(0Y=Rqj62G70J)H=(AL-wL->1!|CH%f5r3R=Qf*9L9 zWrVP`7__8@NE0T(=A2D;l)f?=A_h{cNz{<&CvkwpArePPyh4J0&%pL6vz@(amcFVa zh_l)IX7<{Sy=`E%TvokfRSuR&vowT7am-J$1!rR}+dg1Ko;UwD7%!+i<`4gI+EK_) z0zRMb=iJ^!Ztu^z7yg-h;ip{ZM||wPBkvq9_=-I5tGO~?|YvX7?U*zyn?C|?~=@=iy2=&=!^+VS_Tjbca aQM4bXXYMYxlOD&UZzlcx|B#-=G5-g(l?ZhJ literal 0 HcmV?d00001 diff --git a/backend/services/__pycache__/__init__.cpython-314.pyc b/backend/services/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5f964b21b6a04284c473e15208df7d7c22477f8 GIT binary patch literal 163 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 literal 0 HcmV?d00001 diff --git a/backend/services/__pycache__/weather_service.cpython-314.pyc b/backend/services/__pycache__/weather_service.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71834109d9721459845f935bf0fc61c8c9c64aff GIT binary patch literal 16871 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 SgcnVeYj?uA=l2T8C;VT&U9`IZ literal 0 HcmV?d00001 diff --git a/backend/websocket/__pycache__/__init__.cpython-314.pyc b/backend/websocket/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9680aded46ff7efdcc0f49941e035a0a93900b7f GIT binary patch literal 164 zcmdPq$Zm#Wqg5_1i3&bQ@lA@D7>k(e~Vmvp{U zz6cp8O|bkip^HS_kMa8^XM0cTlgG2kcXcZ&*sj-&q-6`obr2(&xp7<2a=Bz_CaX$o zlHxpkYH)^A#NuMa;!k@lp_OFeL~X=!#YsOG74&UTr4 zddkGV?6HSm+nadH)Tx=6pjyVTl$e&$6R+#$yH+-r7}Im`KZ!{VFpxqket(j&DV6Lhrj@~>^TX2%Bs#+$KwZJEE%#DqX zS?v$O3=_rEd9q4)@xTg->vOE4>!BlAz9`UUCwLE0n1+wdWD_7U&P|lhh$Bj+jL9Q_ z)U&Yn3*5YLO!UEwL}k4Ru{^q3$G9>9Y=W6x#7&O14+;hs<;O2}kL%}^$|YBlmHVRT zrlm~++Ro^yUby^{mQ3n7%a+pFX}CwW%g9V+qddiP(hd;JUOhdIz1*dR><#s%Ds?d{ zMPcG&IPg-Stq|Cl4{W>_PzoMp!J|C#1U~Rz@h^n-EqeB^2*extmnp;OOQ8Yo6KAzrePB&7246L|HW zD=|_73Ah|l{$fuR1u4TUIZ00`S|+6=(>jnsN}0{|jz>kh4py^8pr#q%AF9t}&*`)a zbaV@dsH=(z+nqcE_d-wGZihBZ6V21pqbLqeeAU0QJL;iQ?PX3FhPm_PTV+c@8Okd| z1?5m)Ikc#Z3F$J_=l$TMR~5L=rj7J&}mF*@4pjMb81pVR*aB zHw<@#?kM(vcoB=lc;FJQj|VOhXgdsMxTiayb(5#K46v?6;ZnQ=Q3x#)1tSPiJi#HB zPH@+UmRbFU>3lnTt%XWyub$#xVD>yYsWbu?LMC+pA=4OqaegPlzf>AQu@+DX{7+UU z+~roaj*&PolU;lboWMRca~gq`|^QD^4br^u8b{&_uW2n zduU<(8;kOr-+^xY%J@R((XZOR@-DQ;7Ukpr6QI&pU}N_Ovg^{=OBX2LfGx=a7d4C}JJlqEJ z^58AwzC2uk+o3Prx91ktk1onT0Ad_Wae04VJ;w0Wz`YjtgLpX^VsHgNzW*6YL5hr) zC=%JxcF*Xh34tmocfmKsAcZ2Eyd0G{-$Y|Fu51yvtq>4jbS-0b$93oUpS!;N$)5sD zTaQdU&U^S6UjVjuVynjJV|e@J3pkUj<;LR@e{*&z zxZ&EKrC{qd_p&H#4it&d)_nQM6Paw;`n$meS$P=V_Mx=Q3u^|K!b%~$Js;kFW6!;C ze<9TWb*LXMaC@{EAnko0N=w1e)#i_yKbIDRTZ;myR<;uPm6ZCJzyZr)cc5~R?gY78CoH-tqF$>ISGngLvRCOksnoVOH5~rWfYUxr>v#L%R)U?t@ zM$cqbl>({=?M8tfrh8EALoo`%ZhY}2#fvEM;@62{=6Smq3VkSsX;jg{nYlHW^ Zk$Gv=^(qGxiNfNDzvG^-^OBn_{U2tKy)gg) literal 0 HcmV?d00001 diff --git a/frontend/src/components/EarthTwin.tsx b/frontend/src/components/EarthTwin.tsx index e8a4453..df2e84d 100644 --- a/frontend/src/components/EarthTwin.tsx +++ b/frontend/src/components/EarthTwin.tsx @@ -28,6 +28,7 @@ import { } from '@/types/objectCategories'; import { useLayerStore } from '@/store/layerStore'; import { LayerManagerPanel } from './OrbitLayers/LayerManagerPanel'; +import { useCesiumPerformance } from '@/hooks/useCesiumPerformance'; interface CatalogObject { id: number; @@ -177,6 +178,7 @@ export const EarthTwin = forwardRef((_props, ref) => { const tooltipRef = useRef(null); const [viewerInstance, setViewerInstance] = useState(null); const [containerEl, setContainerEl] = useState(null); + useCesiumPerformance(viewerInstance); const [datasetVersion, setDatasetVersion] = useState(0); const { activeSector, setSelectedSatelliteId } = useUIStore(); const [useFallback, setUseFallback] = useState(true); diff --git a/frontend/src/components/PerformanceOverlay/PerformanceOverlay.tsx b/frontend/src/components/PerformanceOverlay/PerformanceOverlay.tsx new file mode 100644 index 0000000..ff435f6 --- /dev/null +++ b/frontend/src/components/PerformanceOverlay/PerformanceOverlay.tsx @@ -0,0 +1,547 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { usePerformanceStore, type OverlayPosition, type DiagnosticTab } from '@/store/performanceStore'; +import { MaterialIcon } from '@/components/MaterialIcon'; +import { initNetworkInterceptor } from '@/utils/networkInterceptor'; + +export const PerformanceOverlay: React.FC = () => { + const { + isEnabled, + isMinimized, + position, + customCoords, + activeTab, + fps, + frameTimeMs, + droppedFrames, + fpsHistory, + renderedSatellites, + activeEntities, + cesiumSceneUpdateTimeMs, + memory, + activeRequests, + avgResponseTimeMs, + totalRequests, + slowRequestsCount, + recentCalls, + bottlenecks, + toggleOverlay, + toggleMinimized, + setPosition, + setCustomCoords, + setActiveTab, + updateFrameMetrics, + updateMemoryMetrics, + clearBottlenecks, + } = usePerformanceStore(); + + const [isDragging, setIsDragging] = useState(false); + const dragOffsetRef = useRef<{ x: number; y: number }>({ x: 0, y: 0 }); + const containerRef = useRef(null); + + // Initialize network interceptor once + useEffect(() => { + initNetworkInterceptor(); + }, []); + + // Frame rate monitoring loop + useEffect(() => { + let frameId: number; + let lastTime = performance.now(); + let frameCount = 0; + let lastFpsUpdate = performance.now(); + + const measureFrame = (now: number) => { + const delta = now - lastTime; + lastTime = now; + frameCount++; + + const isDropped = delta > 33.3; // Dropped frame threshold (> 30fps baseline) + + if (now - lastFpsUpdate >= 500) { // Update store twice per second + const currentFps = Math.min(60, Math.round((frameCount * 1000) / (now - lastFpsUpdate))); + const avgFrameTime = delta; + updateFrameMetrics(currentFps, avgFrameTime, isDropped); + + frameCount = 0; + lastFpsUpdate = now; + } + + frameId = requestAnimationFrame(measureFrame); + }; + + frameId = requestAnimationFrame(measureFrame); + return () => cancelAnimationFrame(frameId); + }, [updateFrameMetrics]); + + // Memory usage polling loop + useEffect(() => { + const updateMem = () => { + const perfObj = window.performance as unknown as { + memory?: { usedJSHeapSize: number; totalJSHeapSize: number; jsHeapSizeLimit: number }; + }; + + if (perfObj.memory) { + const usedMB = perfObj.memory.usedJSHeapSize / (1024 * 1024); + const totalMB = perfObj.memory.totalJSHeapSize / (1024 * 1024); + const limitMB = perfObj.memory.jsHeapSizeLimit / (1024 * 1024); + const percent = (usedMB / limitMB) * 100; + + updateMemoryMetrics({ + usedJSHeapSizeMB: Math.round(usedMB * 10) / 10, + totalJSHeapSizeMB: Math.round(totalMB * 10) / 10, + jsHeapSizeLimitMB: Math.round(limitMB * 10) / 10, + percentUsed: Math.round(percent * 10) / 10, + isSupported: true, + }); + } else { + updateMemoryMetrics({ isSupported: false }); + } + }; + + updateMem(); + const interval = setInterval(updateMem, 2000); + return () => clearInterval(interval); + }, [updateMemoryMetrics]); + + // Keyboard shortcut Shift + P + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.shiftKey && (e.key === 'P' || e.key === 'p')) { + if (['INPUT', 'TEXTAREA', 'SELECT'].includes((e.target as HTMLElement)?.tagName)) { + return; + } + e.preventDefault(); + toggleOverlay(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, [toggleOverlay]); + + // Dragging support + const handleMouseDown = (e: React.MouseEvent) => { + if (!containerRef.current) return; + setIsDragging(true); + const rect = containerRef.current.getBoundingClientRect(); + dragOffsetRef.current = { + x: e.clientX - rect.left, + y: e.clientY - rect.top, + }; + }; + + useEffect(() => { + const handleMouseMove = (e: MouseEvent) => { + if (!isDragging) return; + setCustomCoords({ + x: Math.max(10, Math.min(window.innerWidth - 300, e.clientX - dragOffsetRef.current.x)), + y: Math.max(10, Math.min(window.innerHeight - 200, e.clientY - dragOffsetRef.current.y)), + }); + }; + + const handleMouseUp = () => setIsDragging(false); + + if (isDragging) { + window.addEventListener('mousemove', handleMouseMove); + window.addEventListener('mouseup', handleMouseUp); + } + return () => { + window.removeEventListener('mousemove', handleMouseMove); + window.removeEventListener('mouseup', handleMouseUp); + }; + }, [isDragging, setCustomCoords]); + + if (!isEnabled) return null; + + // Positioning classes + const getPositionStyle = (): React.CSSProperties => { + if (position === 'custom') { + return { position: 'fixed', left: `${customCoords.x}px`, top: `${customCoords.y}px`, zIndex: 9999 }; + } + const styles: Record = { + 'top-right': { position: 'fixed', top: '70px', right: '16px', zIndex: 9999 }, + 'top-left': { position: 'fixed', top: '70px', left: '16px', zIndex: 9999 }, + 'bottom-right': { position: 'fixed', bottom: '16px', right: '16px', zIndex: 9999 }, + 'bottom-left': { position: 'fixed', bottom: '16px', left: '16px', zIndex: 9999 }, + custom: { position: 'fixed', top: '70px', right: '16px', zIndex: 9999 }, + }; + return styles[position]; + }; + + const getFpsColor = (val: number) => { + if (val >= 55) return 'text-emerald-400 border-emerald-500/40 bg-emerald-500/10'; + if (val >= 30) return 'text-amber-400 border-amber-500/40 bg-amber-500/10'; + return 'text-rose-400 border-rose-500/40 bg-rose-500/10'; + }; + + // Render Minimized Pill View + if (isMinimized) { + return ( +

+
+ + + PERF +
+ +
+ {fps.toFixed(0)} FPS +
+ +
+ + {renderedSatellites} +
+ + {memory.isSupported && ( +
+ + {memory.usedJSHeapSizeMB} MB +
+ )} + +
+ + +
+
+ ); + } + + // Render Full Diagnostics Panel + return ( +
+ {/* Header Bar */} +
+
+ + + PERFORMANCE OVERLAY + v1.0 +
+ +
+ {/* Position Selector */} +
+ +
+ {(['top-right', 'top-left', 'bottom-right', 'bottom-left'] as OverlayPosition[]).map((pos) => ( + + ))} +
+
+ + + +
+
+ + {/* Tabs */} +
+ {(['OVERVIEW', 'CESIUM', 'NETWORK', 'MEMORY', 'BOTTLENECKS'] as DiagnosticTab[]).map((tab) => { + const isTabActive = activeTab === tab; + const hasBottleneck = tab === 'BOTTLENECKS' && bottlenecks.length > 0; + + return ( + + ); + })} +
+ + {/* Main Tab Content Area */} +
+ {/* OVERVIEW TAB */} + {activeTab === 'OVERVIEW' && ( +
+ {/* FPS & Sparkline */} +
+
+
Frame Rate
+
+ + {fps.toFixed(0)} FPS + + ({frameTimeMs.toFixed(1)}ms) +
+
+ + {/* Sparkline Graph */} +
+ {fpsHistory.map((val, idx) => { + const pct = Math.min(100, Math.max(10, (val / 60) * 100)); + const barColor = val >= 55 ? 'bg-emerald-500' : val >= 30 ? 'bg-amber-500' : 'bg-rose-500'; + return ( +
+ ); + })} +
+
+ + {/* Quick Metrics Grid */} +
+
+
Rendered Satellites
+
{renderedSatellites.toLocaleString()}
+
+ +
+
Active Cesium Entities
+
{activeEntities.toLocaleString()}
+
+ +
+
Avg API Latency
+
{avgResponseTimeMs} ms
+
+ +
+
JS Heap Used
+
+ {memory.isSupported ? `${memory.usedJSHeapSizeMB} MB` : 'N/A'} +
+
+
+
+ )} + + {/* CESIUM SCENE TAB */} + {activeTab === 'CESIUM' && ( +
+
+
+ Cesium Scene Update: + {cesiumSceneUpdateTimeMs.toFixed(2)} ms +
+
+
+
+
+ +
+
+ Satellites Plotted: + {renderedSatellites} +
+
+ Total Scene Entities: + {activeEntities} +
+
+ Target Frame Interval: + 16.6 ms (60 FPS) +
+
+
+ )} + + {/* NETWORK TAB */} + {activeTab === 'NETWORK' && ( +
+
+
+
Active
+
{activeRequests}
+
+
+
Total
+
{totalRequests}
+
+
+
Slow (>500ms)
+
0 ? 'text-amber-400' : 'text-emerald-400'}`}> + {slowRequestsCount} +
+
+
+ +
Recent Requests
+
+ {recentCalls.length === 0 ? ( +
No network requests logged yet.
+ ) : ( + recentCalls.map((call) => ( +
+
+ {call.method} + {call.url.replace(/^https?:\/\/[^/]+/, '')} +
+ +
+ + {call.status || 'ERR'} + + {call.durationMs}ms +
+
+ )) + )} +
+
+ )} + + {/* MEMORY TAB */} + {activeTab === 'MEMORY' && ( +
+ {memory.isSupported ? ( + <> +
+
+ JS Heap Usage: + {memory.usedJSHeapSizeMB} / {memory.jsHeapSizeLimitMB} MB +
+
+
80 ? 'bg-rose-500' : memory.percentUsed > 60 ? 'bg-amber-500' : 'bg-purple-500' + }`} + /> +
+
+ +
+
+ Total Allocated Heap: + {memory.totalJSHeapSizeMB} MB +
+
+ Heap Limit: + {memory.jsHeapSizeLimitMB} MB +
+
+ Memory Utilization: + {memory.percentUsed}% +
+
+ + ) : ( +
+ Browser memory diagnostics API is not supported in this browser (Chrome / Chromium required). +
+ )} +
+ )} + + {/* BOTTLENECKS TAB */} + {activeTab === 'BOTTLENECKS' && ( +
+
+ Active Alerts + {bottlenecks.length > 0 && ( + + )} +
+ +
+ {bottlenecks.length === 0 ? ( +
+ + No performance bottlenecks detected! +
+ ) : ( + bottlenecks.map((alert) => ( +
+
+ + + {alert.type} + + + {new Date(alert.timestamp).toLocaleTimeString()} + +
+
{alert.message}
+
+ )) + )} +
+
+ )} +
+ + {/* Footer Hotkey Notice */} +
+ Toggle shortcut: Shift + P + Kepler Dev Tools +
+
+ ); +}; diff --git a/frontend/src/components/PerformanceOverlay/PerformanceToggleButton.tsx b/frontend/src/components/PerformanceOverlay/PerformanceToggleButton.tsx new file mode 100644 index 0000000..c8caa0a --- /dev/null +++ b/frontend/src/components/PerformanceOverlay/PerformanceToggleButton.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { usePerformanceStore } from '@/store/performanceStore'; +import { MaterialIcon } from '@/components/MaterialIcon'; + +export const PerformanceToggleButton: React.FC = () => { + const { isEnabled, toggleOverlay, fps, bottlenecks } = usePerformanceStore(); + const warningCount = bottlenecks.filter((b) => b.severity === 'WARNING' || b.severity === 'CRITICAL').length; + + return ( + + ); +}; diff --git a/frontend/src/components/layouts/MainLayout.tsx b/frontend/src/components/layouts/MainLayout.tsx index 4889c3d..81dbcc1 100644 --- a/frontend/src/components/layouts/MainLayout.tsx +++ b/frontend/src/components/layouts/MainLayout.tsx @@ -6,6 +6,8 @@ import { useUIStore } from '@/store/uiStore'; import { DynamicBackground } from '@/components/DynamicBackground/DynamicBackground'; import { LogbookPanel } from '@/components/Logbook/LogbookPanel'; import { useLogbookStore, logEvent } from '@/store/logbookStore'; +import { PerformanceOverlay } from '@/components/PerformanceOverlay/PerformanceOverlay'; +import { PerformanceToggleButton } from '@/components/PerformanceOverlay/PerformanceToggleButton'; /** Ensures the mission-init System log fires once per browser tab session. */ let hasLoggedMissionInit = false; @@ -239,6 +241,7 @@ export const MainLayout: React.FC = () => {
{utcTime}
+
+ +
); }; diff --git a/frontend/src/hooks/useCesiumPerformance.ts b/frontend/src/hooks/useCesiumPerformance.ts new file mode 100644 index 0000000..d6f9c7b --- /dev/null +++ b/frontend/src/hooks/useCesiumPerformance.ts @@ -0,0 +1,52 @@ +import { useEffect, useRef } from 'react'; +import * as Cesium from 'cesium'; +import { usePerformanceStore } from '@/store/performanceStore'; + +/** + * Custom hook to monitor Cesium rendering performance, scene update timing, + * and active entities count. + */ +export function useCesiumPerformance(viewer: Cesium.Viewer | null): void { + const preRenderTimeRef = useRef(0); + + useEffect(() => { + if (!viewer || viewer.isDestroyed()) return; + + const scene = viewer.scene; + + const handlePreRender = () => { + preRenderTimeRef.current = performance.now(); + }; + + const handlePostRender = () => { + if (!preRenderTimeRef.current) return; + const updateTimeMs = performance.now() - preRenderTimeRef.current; + + const entityCount = viewer.entities.values.length; + let satelliteCount = 0; + + // Count entities marked as catalog satellites/objects + for (const entity of viewer.entities.values) { + if (entity.properties?.catalogData) { + satelliteCount++; + } + } + + usePerformanceStore.getState().updateCesiumMetrics( + updateTimeMs, + entityCount, + satelliteCount + ); + }; + + const removePre = scene.preRender.addEventListener(handlePreRender); + const removePost = scene.postRender.addEventListener(handlePostRender); + + return () => { + if (!scene.isDestroyed()) { + removePre(); + removePost(); + } + }; + }, [viewer]); +} diff --git a/frontend/src/store/performanceStore.ts b/frontend/src/store/performanceStore.ts new file mode 100644 index 0000000..0fa550a --- /dev/null +++ b/frontend/src/store/performanceStore.ts @@ -0,0 +1,247 @@ +import { create } from 'zustand'; + +export interface NetworkCallMetric { + id: string; + url: string; + method: string; + status: number; + durationMs: number; + timestamp: number; + isSlow: boolean; +} + +export interface BottleneckAlert { + id: string; + type: 'LOW_FPS' | 'HIGH_MEMORY' | 'SLOW_API' | 'HIGH_ENTITY_COUNT' | 'SLOW_SCENE_UPDATE'; + severity: 'WARNING' | 'CRITICAL'; + message: string; + timestamp: number; + details?: string; +} + +export interface MemoryMetrics { + usedJSHeapSizeMB: number; + totalJSHeapSizeMB: number; + jsHeapSizeLimitMB: number; + percentUsed: number; + isSupported: boolean; +} + +export type OverlayPosition = 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left' | 'custom'; +export type DiagnosticTab = 'OVERVIEW' | 'CESIUM' | 'NETWORK' | 'MEMORY' | 'BOTTLENECKS'; + +interface PerformanceState { + // Config & UI State + isEnabled: boolean; + isMinimized: boolean; + position: OverlayPosition; + customCoords: { x: number; y: number }; + activeTab: DiagnosticTab; + + // FPS & Frame Metrics + fps: number; + frameTimeMs: number; + droppedFrames: number; + fpsHistory: number[]; + + // Cesium & Scene Metrics + renderedSatellites: number; + activeEntities: number; + cesiumSceneUpdateTimeMs: number; + + // Memory Metrics + memory: MemoryMetrics; + + // Network Metrics + activeRequests: number; + avgResponseTimeMs: number; + totalRequests: number; + slowRequestsCount: number; + recentCalls: NetworkCallMetric[]; + + // Bottleneck Alerts + bottlenecks: BottleneckAlert[]; + + // Actions + toggleOverlay: () => void; + setIsEnabled: (enabled: boolean) => void; + toggleMinimized: () => void; + setMinimized: (minimized: boolean) => void; + setPosition: (pos: OverlayPosition) => void; + setCustomCoords: (coords: { x: number; y: number }) => void; + setActiveTab: (tab: DiagnosticTab) => void; + + updateFrameMetrics: (fps: number, frameTimeMs: number, isDroppedFrame: boolean) => void; + updateCesiumMetrics: (sceneTimeMs: number, entityCount: number, satelliteCount: number) => void; + updateMemoryMetrics: (memory: Partial) => void; + addNetworkCall: (call: Omit) => void; + incrementActiveRequests: () => void; + decrementActiveRequests: () => void; + addBottleneck: (alert: Omit) => void; + clearBottlenecks: () => void; +} + +const MAX_HISTORY_LENGTH = 30; +const MAX_NETWORK_LOGS = 20; +const MAX_BOTTLENECK_LOGS = 15; + +export const usePerformanceStore = create((set) => ({ + isEnabled: false, + isMinimized: false, + position: 'top-right', + customCoords: { x: 20, y: 80 }, + activeTab: 'OVERVIEW', + + fps: 60, + frameTimeMs: 16.6, + droppedFrames: 0, + fpsHistory: Array(20).fill(60), + + renderedSatellites: 0, + activeEntities: 0, + cesiumSceneUpdateTimeMs: 0, + + memory: { + usedJSHeapSizeMB: 0, + totalJSHeapSizeMB: 0, + jsHeapSizeLimitMB: 0, + percentUsed: 0, + isSupported: false, + }, + + activeRequests: 0, + avgResponseTimeMs: 0, + totalRequests: 0, + slowRequestsCount: 0, + recentCalls: [], + + bottlenecks: [], + + toggleOverlay: () => set((state) => ({ isEnabled: !state.isEnabled })), + setIsEnabled: (enabled) => set({ isEnabled: enabled }), + toggleMinimized: () => set((state) => ({ isMinimized: !state.isMinimized })), + setMinimized: (minimized) => set({ isMinimized: minimized }), + setPosition: (position) => set({ position }), + setCustomCoords: (customCoords) => set({ customCoords, position: 'custom' }), + setActiveTab: (activeTab) => set({ activeTab }), + + updateFrameMetrics: (fps, frameTimeMs, isDroppedFrame) => + set((state) => { + const newFpsHistory = [...state.fpsHistory.slice(1), fps]; + const newDropped = isDroppedFrame ? state.droppedFrames + 1 : state.droppedFrames; + + let newBottlenecks = state.bottlenecks; + if (fps < 30 && (!state.bottlenecks.length || state.bottlenecks[0]?.type !== 'LOW_FPS')) { + const alert: BottleneckAlert = { + id: `fps-${Date.now()}`, + type: 'LOW_FPS', + severity: fps < 20 ? 'CRITICAL' : 'WARNING', + message: `Frame rate dropped to ${fps.toFixed(1)} FPS (${frameTimeMs.toFixed(1)}ms frame time)`, + timestamp: Date.now(), + }; + newBottlenecks = [alert, ...state.bottlenecks.slice(0, MAX_BOTTLENECK_LOGS - 1)]; + } + + return { + fps, + frameTimeMs, + droppedFrames: newDropped, + fpsHistory: newFpsHistory, + bottlenecks: newBottlenecks, + }; + }), + + updateCesiumMetrics: (sceneTimeMs, entityCount, satelliteCount) => + set((state) => { + let newBottlenecks = state.bottlenecks; + if (sceneTimeMs > 35) { + const alert: BottleneckAlert = { + id: `cesium-${Date.now()}`, + type: 'SLOW_SCENE_UPDATE', + severity: sceneTimeMs > 60 ? 'CRITICAL' : 'WARNING', + message: `Cesium scene update took ${sceneTimeMs.toFixed(1)}ms (${entityCount} active entities)`, + timestamp: Date.now(), + }; + newBottlenecks = [alert, ...state.bottlenecks.slice(0, MAX_BOTTLENECK_LOGS - 1)]; + } + + return { + cesiumSceneUpdateTimeMs: sceneTimeMs, + activeEntities: entityCount, + renderedSatellites: satelliteCount, + bottlenecks: newBottlenecks, + }; + }), + + updateMemoryMetrics: (mem) => + set((state) => { + const updatedMemory = { ...state.memory, ...mem }; + let newBottlenecks = state.bottlenecks; + + if (updatedMemory.percentUsed > 80) { + const alert: BottleneckAlert = { + id: `mem-${Date.now()}`, + type: 'HIGH_MEMORY', + severity: updatedMemory.percentUsed > 90 ? 'CRITICAL' : 'WARNING', + message: `JS Heap memory usage at ${updatedMemory.percentUsed.toFixed(1)}% (${updatedMemory.usedJSHeapSizeMB.toFixed(1)} MB)`, + timestamp: Date.now(), + }; + newBottlenecks = [alert, ...state.bottlenecks.slice(0, MAX_BOTTLENECK_LOGS - 1)]; + } + + return { + memory: updatedMemory, + bottlenecks: newBottlenecks, + }; + }), + + addNetworkCall: (call) => + set((state) => { + const newCall: NetworkCallMetric = { + ...call, + id: `net-${Date.now()}-${Math.random().toString(36).substring(2, 6)}`, + }; + + const newTotal = state.totalRequests + 1; + const newSlowCount = call.isSlow ? state.slowRequestsCount + 1 : state.slowRequestsCount; + const newAvgResponse = Math.round( + (state.avgResponseTimeMs * state.totalRequests + call.durationMs) / newTotal + ); + const newCalls = [newCall, ...state.recentCalls.slice(0, MAX_NETWORK_LOGS - 1)]; + + let newBottlenecks = state.bottlenecks; + if (call.isSlow) { + const alert: BottleneckAlert = { + id: `net-slow-${Date.now()}`, + type: 'SLOW_API', + severity: call.durationMs > 1500 ? 'CRITICAL' : 'WARNING', + message: `Slow API call: ${call.method} ${call.url.split('?')[0]} (${call.durationMs}ms)`, + timestamp: Date.now(), + details: `Status ${call.status}`, + }; + newBottlenecks = [alert, ...state.bottlenecks.slice(0, MAX_BOTTLENECK_LOGS - 1)]; + } + + return { + totalRequests: newTotal, + slowRequestsCount: newSlowCount, + avgResponseTimeMs: newAvgResponse, + recentCalls: newCalls, + bottlenecks: newBottlenecks, + }; + }), + + incrementActiveRequests: () => set((state) => ({ activeRequests: state.activeRequests + 1 })), + decrementActiveRequests: () => + set((state) => ({ activeRequests: Math.max(0, state.activeRequests - 1) })), + + addBottleneck: (alert) => + set((state) => ({ + bottlenecks: [ + { ...alert, id: `bn-${Date.now()}`, timestamp: Date.now() }, + ...state.bottlenecks.slice(0, MAX_BOTTLENECK_LOGS - 1), + ], + })), + + clearBottlenecks: () => set({ bottlenecks: [] }), +})); diff --git a/frontend/src/utils/networkInterceptor.ts b/frontend/src/utils/networkInterceptor.ts new file mode 100644 index 0000000..701dcd8 --- /dev/null +++ b/frontend/src/utils/networkInterceptor.ts @@ -0,0 +1,70 @@ +import { usePerformanceStore } from '@/store/performanceStore'; + +let isInterceptorInitialized = false; + +/** + * Initializes global network request interceptor for measuring latency, + * response times, active request counts, and highlighting slow API calls. + */ +export function initNetworkInterceptor(): void { + if (isInterceptorInitialized || typeof window === 'undefined') return; + isInterceptorInitialized = true; + + const originalFetch = window.fetch; + + window.fetch = async function (...args) { + const startTime = performance.now(); + const store = usePerformanceStore.getState(); + store.incrementActiveRequests(); + + let url = 'unknown'; + let method = 'GET'; + + try { + if (typeof args[0] === 'string') { + url = args[0]; + } else if (args[0] instanceof URL) { + url = args[0].toString(); + } else if (args[0] && typeof args[0] === 'object' && 'url' in args[0]) { + url = (args[0] as Request).url; + } + + if (args[1] && args[1].method) { + method = args[1].method.toUpperCase(); + } + } catch { + // Ignore URL parsing errors + } + + try { + const response = await originalFetch.apply(this, args); + const durationMs = Math.round(performance.now() - startTime); + + store.addNetworkCall({ + url, + method, + status: response.status, + durationMs, + timestamp: Date.now(), + isSlow: durationMs > 500, + }); + + return response; + } catch (error) { + const durationMs = Math.round(performance.now() - startTime); + + store.addNetworkCall({ + url, + method, + status: 0, // Network failure or CORS error + durationMs, + timestamp: Date.now(), + isSlow: durationMs > 500, + }); + + throw error; + } finally { + store.decrementActiveRequests(); + } + }; +} diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json index 3f21f57..b60a045 100644 --- a/frontend/tsconfig.app.json +++ b/frontend/tsconfig.app.json @@ -2,14 +2,22 @@ "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "target": "es2023", - "lib": ["ES2023", "DOM"], + "lib": [ + "ES2023", + "DOM" + ], "module": "esnext", - "types": ["vite/client", "node"], + "types": [ + "vite/client", + "node" + ], "skipLibCheck": true, "baseUrl": ".", "ignoreDeprecations": "6.0", "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ] }, /* Bundler mode */ "moduleResolution": "bundler", @@ -18,12 +26,13 @@ "moduleDetection": "force", "noEmit": true, "jsx": "react-jsx", - /* Linting */ "noUnusedLocals": false, "noUnusedParameters": false, "erasableSyntaxOnly": true, "noFallthroughCasesInSwitch": true }, - "include": ["src"] -} + "include": [ + "src" + ] +} \ No newline at end of file diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 37e7ede..a2a2b06 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -17,6 +17,19 @@ export default defineConfig({ '@': path.resolve(__dirname, './src') } }, + server: { + port: 5173, + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true + }, + '/ws': { + target: 'ws://localhost:8000', + ws: true + } + } + }, test: { projects: [{ extends: true,