From 4743b47a89db7a126051071c55c59dd095cc7208 Mon Sep 17 00:00:00 2001 From: rdkartono Date: Tue, 26 Aug 2025 08:24:31 +0700 Subject: [PATCH] commit 26/08/2025 --- .idea/deviceManager.xml | 13 + html/webpage/assets/img/green_circle.png | Bin 0 -> 18675 bytes html/webpage/assets/img/red_circle.png | Bin 0 -> 18511 bytes html/webpage/assets/js/script.js | 315 +++++++++++++++++++++-- html/webpage/home.html | 10 +- html/webpage/language.html | 2 +- html/webpage/log.html | 2 +- html/webpage/messagebank.html | 2 +- html/webpage/soundbank.html | 35 +-- html/webpage/timer.html | 2 +- src/Main.kt | 13 +- src/codes/Somecodes.kt | 61 ++++- src/database/LanguageLink.kt | 4 + src/database/MariaDB.kt | 73 +++++- src/database/ScheduleBank.kt | 4 + src/web/WebApp.kt | 75 ++++-- 16 files changed, 534 insertions(+), 77 deletions(-) create mode 100644 .idea/deviceManager.xml create mode 100644 html/webpage/assets/img/green_circle.png create mode 100644 html/webpage/assets/img/red_circle.png create mode 100644 src/database/LanguageLink.kt create mode 100644 src/database/ScheduleBank.kt diff --git a/.idea/deviceManager.xml b/.idea/deviceManager.xml new file mode 100644 index 0000000..91f9558 --- /dev/null +++ b/.idea/deviceManager.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/html/webpage/assets/img/green_circle.png b/html/webpage/assets/img/green_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..0e5d5bbd1d5ad361cae99722bddafa3fe6dd326d GIT binary patch literal 18675 zcmXs!19V)^*FodPcGB2s?4)UI+iuv{ZmhPkZ8tU=+xEt`ll@+Q=l`9YJ)503bLSc} z_eCfvNFpN=AVNSuAWKV$sX#zLGXDF(!vZ6ozOk;r7p#e_q!k?cWFTkBZWNgKpoySpNTdh!-BGZx9fY3({g=)I3(s);)d9JoPWYiwE8c^QBCQ zf=2}pM=lSyjDjx%r%1hI@FU&*<(husxai{5h|OF5-spFTuT`4LdM)(A)np$dg|Wq8 zQ+!D+F3gJ_n2r=?mTmYCe1sq3WM)R?CS5GcWv+eqe<|>l^4(R&m%00PZa!ySpl6s@ zUR4PxeISQm%fYX)Gt#81kD98O`pEHR?MFiT=R^xCYcX3y4oEJ9G?DseYTn+Kbpi_wizNCMi4S!G+>F(QY__Y za`=8oKf>nh5Mdb-xsR6bQ4TVZE#4btHVB9DVMb7+e_rRKAyDSeuGgfxZLu=>t#_E|@ zISbl>hPA{>Sw!;OK>NgdcPt30LYx|0^+tx>XEBqbA8t^#B+Cj)F5&hHl)rUb+1k@1 zYB#g3!6ZDD9G7Y^Nkc61LMlv6^SagP()qP4A?L|d)Q(@>KAd(v;E3r)oXFFKobIi` zI+=I!b=1YHx&9& zkoo}5mARf|E^b4WD{unv$sk=y4>{5Ft2Zti(hgVgiW@lxjx0NP-ac}9<`5{EBpdQ% zGC|I%L09~Nt@fZZxca@spwIegJ2IKd-V5tBOl0)F8UHfHXQt+kgn2&xK4L{iByi$=LFqAcF|q3*7W} zxZTfFLO+K>G~d1ls+&H!6GK$c9J^=1mx8!gjTWD2Z#<|cghVq|0=+26jzz7j5ORY0 zVmu0OuBfa2vgqlv?qzjgG_7%C8_-WHvtAu)F3z>~kES725DazMuy{~N&84YsV}x%O zy>F%4U0x5Uezjf-nG;PzsVM{G`tgffEh>Xj!|PVxdB1kHP~PM0YTY7?rplF zDH*D1lpda?L^Q4T^_=9LnpP_Rb*EGi=GT^PxhhFZCwZ{$V=xF3c58daT>O~!{f=Vk zR5)V9UF{Z4isM>NBQNTLmz2Z!wxpfihgA)h9O)Q79rLgq4_O?V1xbH?|mqntliCqCO%gC zy76u%scqpEqs>a?`<|qdZGMbmgObj+CM`y>6z1QO`F)=4Gve{;i^zlWp6d@&W1El< zCe)!dfhWup@#5EL+5%<4hwxIkhq|n$7ZIxb)e!{mg1B6-{9HUrf(um!Wu=x@Qc_iZV?h*4|GEfeGVRBwD|LzMOGPpE<@EB6Ca zZ3ppJOp$P9L$$45hkyx;5~-@eSCdn?%RxV*H3mDskE@sFKOF+@p^jmX4m3JmpKv=~ z+wP$k0^CUs`RDoH&w1X~LX_VLzbt)cIVR07qH~>*kAg>F`O-@%^(=ioEFfP>O`saX zQJ7{0PcZbDdh}g}W~QKhIDm~tRIW%!tf-0q4qS?w`C4PGtgePFd-qbB-A{nI9B!g! z*3RZPav)|NR6BzAwVyl|H%>Ji5y02jM+djHD9-Fl9twtO(^gz(;VE`gC#~bCrbs93 z6$$IYcu6)PbB#qQff~KhLZENlGqf;?CFwfRK6p9mPn-taH13MD;_i=RvP)=!UX@k!ifg_AKibsK(gQAU52QI7aE z0}tH~35yi`5eKfgD^`7gHiXKAFuT16hQ^WNy{~~%ViU(3bMZW(sRr?2t(g#D4d0A($CV%wBw`!t|F{Nyvpbq{sn$wldu7l&ng7G=tfAlL%8Yv+09vf zm;4kCJI4o9_D_UA+dhX-EEUB1-L>mn^osYxENG8(y_ok|C?gOCx|eXj=JMR^ngfVn zD4z!&=egaOvzZEjejzzk3{@Gqf{v|4I4cT1&r7W+n)YRsq`7>9nQi%LO~*yNkXX`d zWu6dB0k_QF9@g>Io0rcq!K5%;o!_}KYJ$!j}9(MIQ;1_IejeO2TG`K1|EFUROBmcbOr~KVloze zNaT--RH0>XYn;-?#~E+);v+uy}57dY`=N0C_@Ar)fSp)y}jNmj=1-_K5o zH%6^I>-h>zNV18kPd|OWrl1Vhi`d`h%E$s0;b} z<5|+5e#F16c;?j|qu33?t&!W2zS8YMXNJkvdc6?nP zU0~N@ybBRmt9Tgj{W;Q?4u7Nl8ApViz83B|U?|^z{&~H&K4s=kl|J;R@f#Dao8Hj= z!gzgw?q!T&W}VvPb=bl!aKNMHO|CKsLMu^FTF)9P3mb~p)A%CKy4mH7x?!d&y4>$r^ls?b&EAor^8Ij z&`oxuk%Kqvlu4VNL!k&A{I`)%or~YCTTn>A93Bqp{*M&O=Nk+Rm4>X2&oKKH zFU3D^<16g+M)&lSo&8pyIEo0$p%0h~jJw`1_nCvrWChZh)h?VO4Sf^yL?k zPuJa`gQrSf(WK}r3t9hkn|?z>1vCS=O24lK?sC9>JzT8IR+Znce&!my_jkZweEe~3 zEnJj3HMh7hY_yHJ82GaNgM;R!?9vcD3W!c;^5*+9>7~@JbnyaDKMMNT>LG z9ZVSqE>>3M1Iw0x!2`&f-phntzox61H)|`+3ALWu+=8tz?k?0E9=Y>gyY!XqUxJp5 zaGlviD}E~OEzfj@jB4l&qz!Nuye1H)>wGHOup?rMyEXww33%>Gqr{MWiacOz{-AnJ zj_1(@D2JcSPLU${G`DhT^hSq3Tlgq*&Gp4f#1aR+GR!FIaFAC}ZEgM~wf(^Wz`xc|cCP z4i$!Z$|&(ylLx4&K9&~s_ zalDU56NJjkH`EoHU-;umu($cdCW11iXq9)7pP1BTsQK+lcP@L6KOjE%NRUZTujAU9 zi^BnkNSew_s;U>vv5A8o=S$84eus|F126lw%*kR*fIN7HL15$T9}Gg!_fg%Chl@#r zxq`AnHP`NE5QG6siBhjuJa4Doqr};h04-%mN8>?cE(dKbes_0zQ*Mw)V$XfG-zNT0 zf68IiBHA6^pz&;d_!b)V*ZC$|mDK-LBFL1K-lJdk9rn(TkTdMFxx|G~*MhsR0n<_U z$YukkJdrqkc~vi#AOF~uP}vdK6kG$#uE*??1ZXD%FyhM8yBrXhEK{~QrHP^z| z7&!=+K<3p=1K0^)*UbImysG7Rf=05-(YK@RXU@gCDo5ghW>2+=6HN489<#|Q5)y$+ zvX{_@@nIt7h;%8GS7Ayxf0r;ZyX=6&6SD0?VcW7h{VZHrwtvvsL?|wuxt89d_F{$Y zkkdOMRZ^cMuyyJx^}3$D8r|%v;)j|Y=?^k81Um23E>s2l9>fH4X6$gRq{0U5pfOM$qCz51abTIouYT?c?n}m-5c_#B?c;!@^QwitjyEO) zp@zcy5=~lGrlv2#oiHR(-;d}G-!GLH z*BMvV>j5pl!N2Un?LOooUCH?4arxd39tlU(aLO{-{fbD;YM}IgRh^V@mc&bk(hiuoQEC>wys#_k1B; ze72vAphDMrB3+K9M){i2rrhyM7)AH}J?T1f6IY%n^NK45Z^A|>j@!Y99oJQlqa!E? zG=?pH6)oxhmHQ-ZK{<95$bu{zaFV_9`<7|MY-^?yxb-5u_&GhdM67SBSt6!%ZuNU# zQ61ovgQ@+b-rhIM>%sFyGOOU!r;58_PjyWf{T@P%u0Wrk!nKjTd7HWl9c*|&mR0Duc@50@i9cT_Ktvu+|Uq|E@KkSTR;zP@Wq#eN9VI zsLj~LMDDonIY(-BCzEdftJxE4JH%*oav-W@MyEb75xi{|P|)+5A%F2`_HFN?L0o5CMfK4QUI`03?o*?Q4%H3%Oha&6EW zd*k080bnKeh@;r+amdt=tX~js`(OPqa!M=6kM<#?K%mLW88SksjG>pbq&I$q% zzj2G~cd2jl7rQ&Bvj9j3pQ^*opw)#<3Dc>=UTeq}Utf!|Nc69gjlorVxL*s&B}UjU zK3QMGb9Jy^$_Oq>EhR!^!B2pfWz2ttEaVYL*n12{1$={gt9CpeMorXwk5kLo-~ye& z7<4z3SM34DubBO7!0|YS>|8)2(YzBTI!;>~o4IFVJ&UTQv5`e8) zhW{lr06D(C6rZG9;7MWY?8M<9FE(l|?fCAP(FVMCEowTi&=r+}dVL%7pd35GeoRs7 zpTYCeevr7RXFXC*C_;kJ=u=XUCt7~q_5=CC15%A3U^AMK3ki@KvHRhqUwf&*2^(AuRJ{;gZlV(tzC4RJ zbVgN|Rxwlz*NgLj3U6E*i@vuNA`yn1p>X_zj;`!Ud5TtvOE>lAIYd{qvr~cWPsU~ z%@4BYo|!w#T0d5VHVMPgOS6(TrsDc{v>&@WJKl$++MO1BE=L|G)KlV>4b3zAjjyq% z`59e-RSPd;*$v}uX|C#rrT~~8fj=@w5%vHqrx%y2?jAUQd2b2%@$sVRC|O8b=gm9D z@%)8-!+4>_D&4z>H!-iE9c%sRd^tqpnwG19{kqUFp`<=bIduy+v5Dh{_di4$F|Jhou^p|J@O)4uNj zz~-_SdV$iYw!^uI!q;B&T1vT~s?aKHD{Koqog@8#dan>977TeIC?cc@FhpXezs5;h zxZw$`8h&?0Pl6C;@TuI?cAG4^cVKnv%{Nw!{@aZ)eCTV^Dtq>egS^@Lvmaoj1dOERDIz z71{*T*;$E?`}XW-Gpiev*)7%-0SeGq{6!vWHyIQ@fm44{L~m_9r% zo4lfTYGCh=xSN@_r%@#Hb!Q^XqIYEphQX`2EGwg|D_| zPdDW{O@4Ue;hl@?EnZ7dE9&LX7PiJ;-E1gM-kL6K&P``c&%JE;jj_qWB!vRcziHf4 zzs|mcG-91$BWF^q`?!TVOgj$Vdzo?4{qR){8YZwNM+4SyZQmyoM`V$7&6%tK-rY%y zd}v4QYq1p8F}PVSfc0hH*qHMvm#<9UZ_H(W$dHyTlr5$GBKZu<;@gs+2y^G{(A=O7|ZIF}!mua7q!;s~?SJ$Y#V<)DCKV^DuC zw$rCaRXLc?JV}VqQCv+aFC~?6&3s&tV=zsu#g})V>SImAlX^7O)FX2QVdlUE-{3OW zaz7XK#2l~YEP z$-|d_#fXeT=p1|r#!jA{ zpW1`!OLZPCCBXerB@fNxP2*Ju{~2GZcphw*9T-dHEIiidB_OW zjio{x@!1D5H;>M;3Qq(eFbTjeD3aZ4?j&7wme0I#2O(HCj3;F8Tvv@2!y)4S;7=|7 zJCoJKId4D_3REijAW6ExJ?Bly(qNCmhV`OmaxuU=6+NszmHgTFA<_7o8K*%LUe+>R z^w3^CbLTGc7NMOy0;KRW8TJEZ`pp46Vd;*!dyEz`clE{xO_`JN9f}yMk84#Iib_Y% z{HD~6?UPs&FU;1R^WP8hqkV5ycYyB6O^pYI8wZ!q>QKtNk+c@;Q7B+X zp@`Q`uLre5HMpzxhnCP$VzWTCw)vLz=;fyUD*&UZbcll>c_HAswD!x(uQ#H zkMrf)WPMeHF+Ick#sf81m!S+pXW({Ai|uBHI$-dAA8gqo#7N^-z<2V-2aCoitq$X* z784G_T~!l)prBdpkq_kYL*H$8md0SWf=yGLETgz<#i#oOdj=l*6ZH*K;v2`M5jobL zE_Y==p6ob|=ZJ(w0Kh^IS$(95>$9ROGhMjF%)urk?Ul*Xf8a@BOkgY1yHgO;H z#Ex^;s80Cg7#9jOe1=|DghC0=n=8-P#I(F2EYDBDEIPga5?V&}c^_Oco$;Y$b%OBhUFR(sDwLF0$b(F&Y>e&3fbu>e9v zmQ5`8sDs*LSXA3ocyw{B1|-?jKSQWrK8Iwvsp|R5*&gTa4v-fAd8wZ8bE{tKHYs#*q|_oq#l zo-2pnOrjlRuk1W@U1g@>y^sO`CLfy|_=?BJN_u~*4ARS^bTU|j8+#bWzm))aqTWwk zocZ}b@Mf0MN@WM^wlbXKj5QFipOjw&2M9Zl^;PWObmg8h4$L!EjrKM?Hq~w}>Raq^ z`=2cLUlEdlBYuoj@z2^W8Xs^qCh|=Brqb*S9oii%U_wOK9yuC~U&J@AxYrP_aREFA z0X&3GuCnDdt71u#y{bHbk>CAC)Oc_Y3hI_1gjGctUVEIc~ogfGn!yw4X<@^E% z%E~0VNjnfBNRj9ZH!;)C5g}-Q$#zQt^-B*6@b*Ww8=(oZ38j7__pI#Ph#)b-;0$#tK$G+fq9h?sQnaJ(^V-)<+;7Ss-@KTqSu};@E z;bQrlT3c|*6aOF)$)?!AbATdwY}^BbE`S;?T<2nvdXNiD%XR0Fa;=RTnfcWSpIr=5SQrlYlpW9HD86t%bRcYeHD z#?ntJYfn7z60t+x+ErMN_vF5HHKA6^>V>(J^z5SEBQ1Yq++&i25OGmpwqALz|lK76A!dpbs zU}YTj{x#WwCK|k*RK;hKb4Feo{$k&4w{-Tn59Hfa79GTQG~v3W@{fNt+x7EKljj*? zx;9)5gln*{I_8Q{3~{*yc`x|$@o*w+e|S{MrQiZsSSDd*)|rojifC#}|1PFp#U$qm zJbESjk9Uu(-|JJ3M0oW#4ah4tt*BkJcX=h}vlW(z^)kjLO5jp8>1JxD#m5uaxUN4m zA0271*S=R`?2-IiM+`o-)2@jfLC*N{(acLIsU>pXtxnN^z`7G< z$n8%%=qVo`Uuqf?VJk*Qulz9zs~2joSq?93_994EK@K_1%O?sxi0qTc4sh24+yITp z{!%Cyrevd(gPQbko~269)iUk%h=oF$>ZAR)hr{7-eoB;gI`Bon2kNMF~gX9|7 z_^;gk@`@~v$53yWZ+%=v?5olvit=4M8CPe%$=@bKd6;M_bW8|UBN(vqdR;^k^DuIe zYZtQKkj|xXkr3#_sfsL6pWESuY3%^=ZHWVu`EyEn`pOkN@DA_=|$(RheR~C?nDZD z=yY06WmnU}rj&U)R^Pzm7D_f4I^R0_MEKbN-?H7wByvD)bAww5aA3k6n1+}JP!#*o zU-q+xCYDpm_9+OJBN%`Sb07~*fv2IBG&dn!JgvpDkt1gW@B@zefNB`HpBjRD;!UM+ zrPg2q@^fC){b76K6~flr7z`M2iNTRN=2f)5Xh>ojUh8LjucC9D^g8Qexc5k9$L9mp zFG-ge^D<+KSr}%WSH&!<9dWKM#TjYoh$W?i8sv%13N z)_UFHERT1|_&xC#U`yA7Czidv(kSv;-7Jxf(v6J+O|%9$rQP}Jd(W~;)vRhnCM7%^4t5&;(any!09RjOOF7mu+x9lk z9y^4;kC+<({?w=!=T5S^_UX)ZkvM}vl^kdJFagcGLFV+ZtdtUv(KL@Jt5MQL0=%`Hpy`q?^>%D4>+6sO8~d(}jx5D1YVw+A7L^aG8C|Fe>2a)iOWd#1_Dm|7c8TV9#YrSPcVs z(^sZ}3-8(63qxgN=?sN(VL!W!O*)V5KV>x&N$OECOvXfP8u-9txS{8fG4akkJdAjl!UR!~v)K91((GJ{mBDP1V#$0k+4|X>@@_JRKM{W0~5alPqDr~ZeDwPmBwEr3w=wgiI zToVV>@^pL)F^^PRf8Jdil_r*=Wglt0XK9VA=fw`dCto1ys*Oi{5TsxMv<- z&0SNbhqpOgm!x@_75y(--ACh}K})9_(cB*W=2EW>Yx0|BukZYU@eGVGdA6~AhZ^at za4PBI=gGmPe+3oJMZGMpGo@HKeVdhMWHmNJG6W+1Yt?a7?Xvi4hvvFH@WOs@WuO$Z zq7?gI@PwNneND45gd6lt*3EIl8G^0?AWob^0|a8qh> z)wdG^IX&sC$J34-7Uz^T_2;K<@wD`a2l1KWG`?D2uS;z{&SoRTr@Z@F3)q%@3LZ8> zS9k+UH#rxrhGCi#ss?_bf$cUWRIGl%0t^PM7?Q-_RlrVxn8p`b^t{?I*Mjfz%t}m3 z<_+R5V)l#}CmCwHDt|U@Xx-A4dyPc!QN>OUmzx~OM&zM@{Tl)9(lCN<++w z7vAoRu-7N!y(#32btQ8MWoiHL_;4DtX|xmAB&VTOhE@#qI@8b9hT1-)t`#+OyA=15 z&MO;&Yh3rzoxnmZ1LIN1)_qGBP8M?&%5EC6{w#%3C9- znrrrF5j;BgK*+J4tX$0#rRzvEm`ZqzJ*{R4yA&XL|Lk9?p@mWCOL<&-)OkFtLOkeb zJ>IwELMk;!p}YdeI)5!wcO3U%Rn)W~?*c3>N;Q}xy?`7KGNEn@2Kw$?xqJ&2&YXcK z7PFGw43*FX)5D0e5sOw%&CDvFK z2k-GH_$qfZd5Ra>reDrhw zhbdc;^eaRJ$}-nBqCrOk=}(u42vZipGZlX5eVLdOjv0KEICoJw9E6ni8eQFNyvsEp zo~RfR&ioyV;8G3{uJt7j3p0) z8?O)6dX4`_ENG5}6FnUHqxE_M?4aXux>ydEF`E1*`4)e^$R!VejU)%xg(w=ivxbikq#7cFz2LWM3Q_7GZ$KadzhqLOR-2dB6 zJ0*09l&$am=a1C?pE4!~o8= zKgH;{8+rh}GZUng38DQ8i}y(;WT%J=(2+1P-*6RjRx=T}g5Q-!`bGebVHI&|f3T+@ zgGY?%xRb)6+*|)akAhGRNFbCgA>uOG!!veL8+p><9{;B`1sP$iy!0Be_&EXcx6-Ah z3ujJ5gPt&SoIvX@BuubU0xgQAxq5UI}3D(I<#$tI! z+0pI`t-k*NnYCj^1oB2L9(`TEG z)&0lDRwKc()ZGhU5Hh9r<|5rE|K~c}w8l@`5?6M>P=C^jP_H1;wkgvJfQ1y~+Ev_A zMNfDVqrHo#95Yrha(?$<=?p5n$-jB9)nI+`Dm9D6w@3D2GhTevLWX$uSd-D`fk`}kZX0vCLJ>fB7MyY2!Rog zz3Vw?r;j4r{SWNgPJ%WPZIbKXx&T$CLSRpsK3Cq-o*ItkE0u*07*U{Wyd4!QC-iV9 zD(3oNmWK`e&i`|>YnXYHQ-FA&TKTv4jP%YsHjsMSY6`*j08zmWMGxmG}E_4K#&t`Jwg|#PCSs8taAQh zaoMiJ4LD8np^sT`g=-J$N9uxS?$yophV&S)acQ8Fx$!|KBFiuRLi>+=R3AzDS+Ets zcfk6!_rT$cU1j!q3n>}TMJ5*_OJjH`x;T6t_7B&z85z>*)qV>I4E%*t^`r6*--#^A zQ@7Qnay@bH=bkiYyZrkV)qXAUEAYOg;A{Cmrz8U={;I?ZeUh8u2{>S$=aEZKaU*PZ zA2eiY#s%-Gj~SW?GK~xFe*sKI=(WeXJ;f^?a$qqVn?b?V&13iqVkJ2O)D-J4<{pKF z{}A>=7`l`(EASQ`F||Bi=@>{=-K$9;Ai0dScUu=7blgu4a(7`@vG#!2y-a1kKMquf zYdwwVUpJ>{w4k6u(ae7eVpDu3TLrsb=25C72w>?kUO{1ANGxgb7?r`4ZHns$t{ zVH$gfRdCuG0z_6pI?K<4UWk3~f*yM%%{N~y+fPr_wBpx$x6k#Rd@BFjrs>1(qmEg# z_oABnWkbs1dX7BFFO~E3jqRxJ&iDR*;VX^PBaB5PWLLAE0WypU6O!9=_>t$~P>OgY}Oylb!qO23=+p<%XfHua? zo&A@A6_PJ|sdb*F7pKo22A&zLk2KDYy+38j79OS@GYWHl_Y!roQht0#s21?!()3*K zrtf@pR^9X){8+VY9$Wn%A1x%ii424B_3DrwQ8IpWTDmOcALea9((QNc5jzE60ObD$ z5N8&Rd~%5Jf}6OvwoDZ0PZo~$>c_L`JSSdeqcyef06e&bl*qu!Rje(3Us`p!W^@#b z|6@g!S8U;L0rLE_%^MVZSN2Wf!Bl*hi>>}4tHFl=nu)!#101a)6*3vWVg6=U(a4yp z5htR9HF&99^o#BVguK3FR^x5GD!&Clm+x-mCVaXUi7x!ch}=|(hz5Dx_b*{mauefn z*O2&9nJhimojWpPO8B`P&pF8%ygYgS)^ph>3H_SG}MEuQzAJik)-A zl;`_&3!^ineXF>C;&XQUAZLjBZ=jp|i{eB=9b5hrLK?rDlnk=r-MxW5LVUvp;9ePF zjWHZH_mZ5>%hT`$IO63qoB;~};}%SPNUBcVkYDW((lqCdz2W=N-bo^NF>t2UNnHpV z*#;ivb+8y<7ooa$ll~0$Z`WVcqW@Cq9tOn53XH9@)&Ut{l=9wf+=ZPTKrhQsES^gI z$$NJ3CTOu~T3s7RS@Ye44Z|QFi00Lk)k|OJOHuA5z#S$A@OqFNY=&MyyJNzQ6}1Mnl+-}Txr962 z-@H$pM6ylqijF!a@k6Fs^=^9V{{n_ntR!x#6u3{i>CfNHspCGswjLS2**Pp?=P=h2 zS!%2Hth&M!`QJ4uccSp6V%M=tGF;K6SpA@V%;QPL$+6BT_tZ{gKqE8uB)V_yx=-6YaBH8~Ih6~6InIqwyapNikXgwQ zMJ$gj8+g*6PyW$_1W9LcW!V1tH!ut_Ie9nJ=S-kg_(lrDqOw&z{wwRHBe~iI`ZBgo zuVkykhiYGH@~zttB(Ad}%&p%;ck}(35T@4mM^AA_=Hh+&yZXP~3l+L=t9w^H?Szdl zzxCaZ4@Qvg`}A)lJq`Y zPBiIYuzRp)t+F}fk(EfGyn5r_Ar=5ve-R3+RX*yj9$OtBKRKPS+9i2?JwspcUXE^P z|1xX3zT)N~b@29>YIRHUTC@t`CQG+UR(MKi`@sD5Q^o`T z0nda$N(Nb)$179qkuDf6{2gXsWv0h4{UP1pC-i&X^u-qUhOu&;{6IQ$PZZY@_KSA7 zm>ZF0Y-x}GyF`0(RXf}Xm_R~OHf&$G!ic=}2hI83?vI&Wnhs*k3-&Osma1G3BNx>E z6m>f=Kq2Sn)JYwlH5|lLs#N*M9(ww#inJu#f>Zg~S~8iwRt^6N&jcgY)%(ax_UsiO_vam`nZgQd<;DkEFMe#LhvcF&vL zmaJ+xY!ya<&`8u@8T@r&Y_iz%nRL+wCu1ym6*B8c-s`wQ7tPoFeeaU2ZaFR<#(xoU zTt1$dA?Cp$1jl)=R(TGub~JE}e>fHi`ljsdOmaDEPM-j#hbVg-& z#fV)D{N=(UWu-W4`ljokLnvYW2NUqU(2&k9Oc9b70?heLr1H0SKBN0^w8ZZo@@cAd zuF?6m7bY6^Fw>bTV^n4pM_#K9lN9P@>hZwB)PQKNc#lUohW(}ENu$&3)z<(f;`f*| z&e9^JYDv@0euw6DsL5fES$ewtM(af(ZukqH2CfFL!+Zygi|!|XxNGSj5i`G5La^s} zYK-XX?HO|SUn~k-qx$^f&Q{`dg-0E&#R6ERcZOFl4O&liwZO?}f;z{Q^4ynZoCGey zF@0oS0kl`j4ugq`kCTQ66RjYg1`KcD->@do2u(N!srW5~3Mq`3et{^*z2NI>rP283 zojw%a4M*ssD-FAoa@ScR%T)CC%;X(^@IrE7!i#nS%@t!IMRtkn-vB-Y#uUWL^y&L# zlp&=DV`rcK@f8n&;&JE(`!o`M5H!CWsG_F~I88 zo3hQn;yX1o&~(3hcf#8#bIl?D_%5eK~?KZCJc_+ z*U`!KGWbbZa?M|$%*Agf(;|ctHtQUN+G!u;HmzU7HrOm+TZKXnvjZgxJD(95O8#~S z9Zf|BUW|$vn3sTb>`vsZ>?;=Joihr>yNON){4e2<_79IYzux+j>r@Q_s39|^pj5(p z$P5tr;@fwvxDj0q%gq7BZXE~wkP%G;oPUj(*W$b47JE33tmFxMMNTX<5?R_KiQjn= znuq8>8k@!}eXFNNATeYy}t2qBSo{Q{{at$;vRzB1OiKA(;;_EEy%81y41fXV7y0pu`rpzcil=|w*?$|qaK%snh)3E(VU)aOP(Q( zM6%6vsQ+TT+$Q}i+=dEkhmAk!BUWC16kUbUFBlxTkcDyVAA@EBq@&%D3=QANrrLF7 zM;#4-sMwAJ?%^LCn@-G@c;v_f6`F!O7MS=gqnQ7Z{&LCA9abVhj`@^#bkPc!n#u}? z+DQ>$&*)?CF_a`;vo%K-?^u^`IUy?G9V7;=fg<;-w8 z4R0m7ilf?{)Qff3Sskz!5$T4%YX`u2)bAK*i7&$1Ckx{vc_@ql&EHA=?s2Gu8@~=* zPo|l!wVXYl5i$lh*s06qPT; zD20VlzZbsE>GXa6&O*3*5;7$Iw*mh{lsh-QYX`O8!Zga4l%Eq`{L2?A(=lZ~)j;g@ zzIi;IK+MM7ohRou;^3dIiGqG!6bbT-1|I3hfVWT+#yeyDWlzCuz04TqBX8MduCx2m zbFGj!Xol(i;PD=Z{^i|CI&dzU|4(-QL&FU+}HLzFTntzpl4- zE}*jPht;%bqM{zQE$!(ko=fC*-O@Maab**%W*hT=hFh--0H7O^L)U$n zEU(Z-kS!cgBj7f)Sm7pKplZRRCCh=Lo|o>{>%E>`FQcMU*MtzfYJQ^5`J@fFf*s5o z&y;C&&5lf#j%YtcVJ})3KC|dGDh>d>p|?@p9#4-mm1PQz1q>DO{rxXjs@&=#*$Ga^ zj?VSoi60xQDTGbdW=1DWjGi@iggv}>?KYq7=q&soYr*g;bf`>#<1)w6f^a2pt)77kk$eQezJ1ryx4WrDE^Z;M%< zclT6yJxQX`dt7E!c z!Nsqh#vvkuy40o7DUJ`)?r06{lSP}5?L^@1bhxox&TJZ)*T{V&RM|txz3n|+^~^DK ztzv{UKV-6qv{k7jVGB*-X7bM4;UD_05I*i!aqNR1!hL{s9+P_bXA%WD)clS%p}^+-ph{BZqVneeUgIp7wm;-?32#$yncgQ7ew?+c#eZr!(p z?_uwQX*2`AV<$T^7j3)|j)UyF;OpLc{qU`y5NjWbrP`7Ek|gz&G9!>G>Hq-9KOh*m zf5Q74@Oxi`Hwnc@Os>L|{zhUtIE7b|**-h!@jjmkB06ggW4#K)^F=GvBXM2tS_DCz zFv5(J2FqZyH=}#NmCl6OKID$>AZ05qHF%TLwrx2XWJ2sg)cFf;MB#?*50w?u6$+vH zWG)esYv&5w9_chWDSr=ijSU(wgkizRGvhwgFNnHmU_dn>GJI_RzW|d4Z250j?}MP? zg3GCzoKIyAuETrr;>a9c{XE6=-+N8uZ;{3O`|2 z9oQ0I_?&N79XK%+!iCrJGWCqAB&zTHy|^_pht_PTkoh9WR}gy;6?NUenQ?(NOOxYU zf|YfMzK86@kK0xIH^-Z|dv?_UNKdXQ?}BS-n|dY{Q`Ml_xg|D-*6w5?`?sik8Ke79 zTu{xD3#>UZS#cTEoCo?&(uH^16}=bQjXMLoZV#Fpo1Dp9>y|omYVvO?;|-X7=V4yn zTsD1OFH+2W1>|dp-GhpCK{YpAV9l{0|LcGXDqX-^2+Hr#w*G744KL+v&r4Jn4zw<~ z7CQl~L57V;i+Ee=!9mQsK@x&pJeN6r5A$7Q(rdxVL<1MVdm4iR~{ z!Swmz0&AWH%^!nO8vHN=T#2#o(9Fb}wX<)7UAHG`*X~6dM^2on+J#qrnR&< zF0g7KX#7<)RYnTw9B>K7zKwL@&9-%5qpm!Zv8(r^?Z?oR3$F!pa!8d@NPh?B;9BJH zs)8A8dzoV9->~MNfoC;S9G6nyx_vd^0;|TVRhN3G=>)w7FTO+5g`2f?aJ{ZPSPieX z{%Uxc$+fv$c+Cx&{IruHHPAj8QmYFx*t%DWnfr*ndq5sT>`_!=S54KH3#>X4G<+Pu zM_GYPhF7L=leP@3*RCUJ+jY2Fd1VUK6T(gfmkY47BPV?geWZ}7R#Lqyk>2j29B+*Vma11BICK2DWxC9nujJssuv|JkGqhnM=r1y zfywalP-#N!X5d;9mFqNJyj+`)uh8~m8Ex&ab{+F0)c{Mxrv}$j4>39I`_QWf)Mz6z z(2fkYBmEr(8EWa1V*2}p$wz=kQL_`#VU*Z~RQ+>-wa5hdU#m`m2oq_b3(-pvy@X`t z5)Dch&@k3%TLyC4Hej^v7}_*hfh8*7+v-LZdnG2PeT@u+$!b6iHzNZb$UvJl$J@pk zYd9+9)HW)~XHj_;m1lrn5@C_p!_7cnTwGu+azXAln4DRkMrfU^z_#K?TS=8J)}XXf z^P@S<52v+p#AxFX+B5{&aWoNDmwoo^s~FBi+f0xdo&rYCq2=u`O%DKCIT`A(d;-=pJWP<2vIKv6ITx_T2sX&$x1?#6`y2T zG+C}LRG2KI$x1cEypsd1`n`J9uBXH<EaW;_CNONkS!na)49hyp6m$Y4E>HYct#`4r09;@#b3x|6LiKSFAY_0H zV%rc_fwlw9lb=n9HsMFjcyTi(ZbV2SngC6zXaXS#nyCH^Pzgaol!{cpub>j5l(60i zu{T8I51}#)3ePw7WFgqi`MfKmX;fR+m`n3l8u3s43#C6(nw$^ZZWC3HntbYx+4 zWjbSWWnpw>05UK!Gc7SNEio`uF*G_bIXW~nEigAaFfcplQ|$l%03~!qSaf7zbY(hi zZ)9m^c>ppnF*7YOFfB1KR53IMzCV_|S*E^l&Yo9;Xs0000LbVXQnRB3cV!Z literal 0 HcmV?d00001 diff --git a/html/webpage/assets/img/red_circle.png b/html/webpage/assets/img/red_circle.png new file mode 100644 index 0000000000000000000000000000000000000000..5c73ecfe5799c0d8f6f4fb8c2a28aa0d66e71328 GIT binary patch literal 18511 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 JPDHLkV1n8Gw`2eS literal 0 HcmV?d00001 diff --git a/html/webpage/assets/js/script.js b/html/webpage/assets/js/script.js index 636c33f..57cab44 100644 --- a/html/webpage/assets/js/script.js +++ b/html/webpage/assets/js/script.js @@ -1,38 +1,209 @@ -$(document).ready(function() { +$(document).ready(function () { document.title = "Automatic Announcement System" - + $('#onlineindicator').attr('src', '/assets/img/red_circle.png'); + + let soundbankdata = []; + let messagebankdata = []; + let languagebankdata = []; + let schedulebankdata = []; + let logdata = []; + + /** + * Fill soundbank table body with values + * @param {SoundBank[]} vv values to fill + */ + function fill_soundbanktablebody(vv) { + $('#soundbanktablebody').empty(); + vv.forEach(item => { + const row = ` + ${item.index} + ${item.description} + ${item.tag} + ${item.category} + ${item.language} + ${item.voiceType} + ${item.path} + `; + $('#soundbanktablebody').append(row); + }); + $('#tablesize').text("Table Size: " + vv.length); + } + + /** + * Fill messagebank table body with values + * @param {MessageBank[]} vv values to fill + */ + function fill_messagebanktablebody(vv) { + $('#messagebanktablebody').empty(); + vv.forEach(item => { + const row = ` + ${item.index} + ${item.description} + ${item.language} + ${item.aNN_ID} + ${item.voice_Type} + ${item.message_Detail} + ${item.message_TAGS} + `; + $('#messagebanktablebody').append(row); + }); + console.log("loaded " + vv.length + " messagebank items"); + } + + function fill_languagebanktablebody(vv) { + $('#languagebanktablebody').empty(); + vv.forEach(item => { + //TODO examine JSON structure + const row = ` + ${item.index} + ${item.description} + ${item.language} + ${item.aNN_ID} + ${item.voice_Type} + ${item.message_Detail} + ${item.message_TAGS} + `; + $('#languagebanktablebody').append(row); + }); + console.log("loaded " + vv.length + " languagebank items"); + } + + function fill_schedulebanktablebody(vv) { + $('#schedulebanktablebody').empty(); + //TODO examine JSON structure + vv.forEach(item => { + const row = ` + ${item.index} + ${item.description} + ${item.language} + ${item.aNN_ID} + ${item.voice_Type} + ${item.message_Detail} + ${item.message_TAGS} + `; + $('#schedulebanktablebody').append(row); + }); + console.log("loaded " + vv.length + " schedulebank items"); + } + + function fill_logtablebody(vv) { + $('#logtablebody').empty(); + vv.forEach(item => { + const row = ` + ${item.index} + ${item.date} + ${item.time} + ${item.description} + `; + $('#logtablebody').append(row); + }); + console.log("loaded " + vv.length + " log items"); + } + const ws = new WebSocket(window.location.pathname + '/ws'); ws.onopen = () => { console.log('WebSocket connection established'); + $('#onlineindicator').attr('src', '/assets/img/green_circle.png'); }; ws.onmessage = (event) => { let rep = JSON.parse(event.data); let cmd = rep.reply let data = rep.data; - if (cmd && cmd.length > 0){ - // console.log('Command:', cmd); - // console.log('Data:', data); - switch(cmd){ - case "getCPUStatus" : + if (cmd && cmd.length > 0) { + switch (cmd) { + case "getCPUStatus": $('#cpustatus').text("CPU Usage: " + data) break; - case "getMemoryStatus" : + case "getMemoryStatus": $('#ramstatus').text("Memory Usage: " + data) break; - case "getDiskStatus" : + case "getDiskStatus": $('#diskstatus').text("Disk Usage: " + data) break; - case "getNetworkStatus" : + case "getNetworkStatus": $('#networkstatus').text("Network Usage: " + data) break; + case "getSystemTime": + $('#datetimetext').text(data) + break; + case "getSoundBankList": + let $soundbankfilter = $('#findsoundbank'); + $soundbankfilter.empty(); + soundbankdata = []; + fill_soundbanktablebody(soundbankdata) + let xx = JSON.parse(data); + if (Array.isArray(xx) && xx.length > 0) { + soundbankdata = xx; + fill_soundbanktablebody(soundbankdata); + $soundbankfilter.prop('disabled', false); + $soundbankfilter.off('input').on('input', function () { + const filterText = $(this).val().toLowerCase(); + if (filterText.length === 0) { + fill_soundbanktablebody(soundbankdata); + return; + } else { + const filtered = soundbankdata.filter(item => + item.description && item.description.toLowerCase().startsWith(filterText) + ); + fill_soundbanktablebody(filtered); + } + + }); + } else { + $soundbankfilter.prop('disabled', true); + alert("No soundbank data available"); + } + break; + case "getMessageBankList": + messagebankdata = []; + fill_messagebanktablebody(messagebankdata); + let yy = JSON.parse(data); + if (Array.isArray(yy) && yy.length > 0) { + messagebankdata = yy; + fill_messagebanktablebody(messagebankdata); + } else alert("No messagebank data available"); + break; + case "getLanguageList": + languagebankdata = [] + fill_languagebanktablebody(languagebankdata); + let zz = JSON.parse(data); + if (Array.isArray(zz) && zz.length > 0) { + languagebankdata = zz; + fill_languagebanktablebody(languagebankdata); + } else alert("No language data available"); + break; + case "getTimerList": + schedulebankdata = [] + fill_schedulebanktablebody(schedulebankdata); + let aa = JSON.parse(data); + if (Array.isArray(aa) && aa.length > 0) { + schedulebankdata = aa; + fill_schedulebanktablebody(schedulebankdata); + } else alert("No schedule data available"); + break; + case "getLog": + let $logfilter = $('#searchfilter'); + logdata = []; + fill_logtablebody(logdata); + let bb = JSON.parse(data); + if (Array.isArray(bb) && bb.length > 0) { + logdata = bb; + fill_logtablebody(logdata); + } else alert("No log data available"); + break; + case "getSetting": + console.log("Setting:"); + console.log(data); + break; } } }; ws.onclose = () => { console.log('WebSocket connection closed'); + $('#onlineindicator').attr('src', '/assets/img/red_circle.png'); }; // ws.onerror = (error) => { // console.error('WebSocket error:', error); @@ -43,7 +214,7 @@ $(document).ready(function() { * @param {String} command command to send * @param {String} data data to send */ - function sendCommand(command, data){ + function sendCommand(command, data) { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ command, data })); } else { @@ -51,43 +222,133 @@ $(document).ready(function() { } } - setInterval(()=>{ - $('#datetimetext').text(new Date().toLocaleString()); + setInterval(() => { sendCommand("getCPUStatus", "") sendCommand("getMemoryStatus", "") sendCommand("getDiskStatus", "") sendCommand("getNetworkStatus", "") + sendCommand("getSystemTime", "") }, 1000) let sidemenu = new bootstrap.Offcanvas('#offcanvas-menu'); - $('#showmenu').click(()=>{ + $('#showmenu').click(() => { sidemenu.show(); }) - $('#soundbanklink').click(()=>{ + $('#soundbanklink').click(() => { sidemenu.hide(); - $('#content').load('soundbank.html'); + $('#content').load('soundbank.html', function (response, status, xhr) { + if (status === "success") { + console.log("Soundbank content loaded successfully"); + sendCommand("getSoundBankList", ""); + + } else { + console.error("Error loading soundbank content:", xhr.status, xhr.statusText); + } + }); + }) - $('#messagebanklink').click(()=>{ + $('#messagebanklink').click(() => { sidemenu.hide(); - $('#content').load('messagebank.html'); + $('#content').load('messagebank.html', function (response, status, xhr) { + if (status === "success") { + console.log("Messagebank content loaded successfully"); + sendCommand("getMessageBankList", ""); + } else { + console.error("Error loading messagebank content:", xhr.status, xhr.statusText); + } + }); + }) - $('#languagelink').click(()=>{ + $('#languagelink').click(() => { sidemenu.hide(); - $('#content').load('language.html'); + $('#content').load('language.html', function (response, status, xhr) { + if (status === "success") { + console.log("Language content loaded successfully"); + sendCommand("getLanguageList", ""); + } else { + console.error("Error loading language content:", xhr.status, xhr.statusText); + } + }); }) - $('#timerlink').click(()=>{ + $('#timerlink').click(() => { sidemenu.hide(); - $('#content').load('timer.html'); + $('#content').load('timer.html', function (response, status, xhr) { + if (status === "success") { + console.log("Timer content loaded successfully"); + sendCommand("getTimerList", ""); + } else { + console.error("Error loading timer content:", xhr.status, xhr.statusText); + } + }); }) - $('#loglink').click(()=>{ + $('#loglink').click(() => { sidemenu.hide(); - $('#content').load('log.html'); + $('#content').load('log.html', function (response, status, xhr) { + if (status === "success") { + console.log("Log content loaded successfully"); + const $logdate = $('#logdate'); + const $searchfilter = $('#searchfilter'); + if (!$logdate.val()) { + const today = new Date(); + const dd = String(today.getDate()).padStart(2, '0'); + const mm = String(today.getMonth() + 1).padStart(2, '0'); + const yyyy = today.getFullYear(); + $logdate.val(`${yyyy}-${mm}-${dd}`); + } + $logdate.off('change').on('change', function () { + const selected = $(this).val(); + if (selected) { + const [year, month, day] = selected.split('-'); + const formatted = `${day}/${month}/${year}`; + sendCommand("getLog", logRequstData(formatted, $searchfilter.val())); + } + }); + const selected = $logdate.val(); + if (selected) { + const [year, month, day] = selected.split('-'); + const formatted = `${day}/${month}/${year}`; + sendCommand("getLog", logRequstData(formatted, $searchfilter.val())); + } + } else { + console.error("Error loading log content:", xhr.status, xhr.statusText); + } + }); + }) - $('#settinglink').click(()=>{ + $('#settinglink').click(() => { sidemenu.hide(); - $('#content').load('setting.html'); + $('#content').load('setting.html', function (response, status, xhr) { + if (status === "success") { + console.log("Setting content loaded successfully"); + sendCommand("getSetting", ""); + } else { + console.error("Error loading setting content:", xhr.status, xhr.statusText); + } + }); }) - $('#logoutlink').click(()=>{ + $('#logoutlink').click(() => { window.location.href = "login.html" }) + + + /** + * Create log Request Data + * @param {String} logdate in format dd/mm/yyyy + * @param {String} logfilter + * @returns JSON string of log Request data + */ + function logRequstData(logdate, logfilter) { + if (logdate && logdate.length > 0) { + const dateRegex = /^(0[1-9]|[12][0-9]|3[01])\/(0[1-9]|1[0-2])\/\d{4}$/; + if (dateRegex.test(logdate)) { + // logdate is valid + return JSON.stringify({ + date: logdate, + filter: logfilter + }) + } + + } + return "" + } }); \ No newline at end of file diff --git a/html/webpage/home.html b/html/webpage/home.html index 9a10c6e..bce5b87 100644 --- a/html/webpage/home.html +++ b/html/webpage/home.html @@ -60,7 +60,15 @@
-
+
+
+
+
+
+

CPU Status

diff --git a/html/webpage/language.html b/html/webpage/language.html index af707ab..5f6cf73 100644 --- a/html/webpage/language.html +++ b/html/webpage/language.html @@ -29,7 +29,7 @@ Filename - + Cell 1 Cell 2 diff --git a/html/webpage/log.html b/html/webpage/log.html index 4664016..3a79fb0 100644 --- a/html/webpage/log.html +++ b/html/webpage/log.html @@ -44,7 +44,7 @@ Description - + Cell 1 Cell 2 diff --git a/html/webpage/messagebank.html b/html/webpage/messagebank.html index 5dd1033..d563ee3 100644 --- a/html/webpage/messagebank.html +++ b/html/webpage/messagebank.html @@ -29,7 +29,7 @@ Message Tags - + Cell 1 Cell 2 diff --git a/html/webpage/soundbank.html b/html/webpage/soundbank.html index a584771..da10250 100644 --- a/html/webpage/soundbank.html +++ b/html/webpage/soundbank.html @@ -15,6 +15,25 @@

Sound Bank

+
+
+

Search

+
+
+
+
+
+

Table Length : N/A

+
+
+
+
+
+
+
+
+
+
@@ -29,7 +48,7 @@ - + @@ -52,20 +71,6 @@
Filename
Cell 1 Cell 2
-
-
-

Search

-
-
-
-
-
-
-
-
-
-
-
diff --git a/html/webpage/timer.html b/html/webpage/timer.html index 66326eb..d2a8fd3 100644 --- a/html/webpage/timer.html +++ b/html/webpage/timer.html @@ -29,7 +29,7 @@ Filename - + Cell 1 Cell 2 diff --git a/src/Main.kt b/src/Main.kt index 5447ca7..865b54a 100644 --- a/src/Main.kt +++ b/src/Main.kt @@ -1,20 +1,31 @@ import audio.AudioPlayer +import com.sun.jna.Platform import content.ContentCache +import database.MariaDB import org.tinylog.Logger +import oshi.util.GlobalConfig import web.WebApp fun main() { + if (Platform.isWindows()){ + // supaya OSHI bisa mendapatkan CPU usage di Windows seperti di Task Manager + GlobalConfig.set(GlobalConfig.OSHI_OS_WINDOWS_CPU_UTILITY,true) + } Logger.info("Application started" as Any) val audioPlayer = AudioPlayer(44100) // 44100 Hz sampling rate audioPlayer.InitAudio(1) val content = ContentCache() + val db = MariaDB() val web = WebApp(3030, listOf( Pair("admin", "password"), Pair("user", "password") - ) + ), db ) web.Start() + + + } diff --git a/src/codes/Somecodes.kt b/src/codes/Somecodes.kt index a004dda..b55e81d 100644 --- a/src/codes/Somecodes.kt +++ b/src/codes/Somecodes.kt @@ -1,20 +1,26 @@ package codes +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch import oshi.SystemInfo import oshi.hardware.CentralProcessor import oshi.hardware.GlobalMemory import java.nio.file.Files import java.nio.file.Path +import java.time.format.DateTimeFormatter +import java.util.function.Consumer @Suppress("unused") class Somecodes { companion object { - + val current_directory : String = System.getProperty("user.dir") val si = SystemInfo() val processor: CentralProcessor = si.hardware.processor val memory : GlobalMemory = si.hardware.memory - + val datetimeformat1: DateTimeFormatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss") const val KB_threshold = 1024.0 const val MB_threshold = KB_threshold * 1024.0 @@ -36,8 +42,59 @@ class Somecodes { } } + /** + * Get Disk usage using OSHI library. + * @param path The path to check disk usage, defaults to the current working directory. + * @return A string representing the disk usage of the file system in a human-readable format. + */ + fun getDiskUsage(path: String = current_directory) : String { + return try{ + val p = Path.of(path).toFile() + if (p.exists() && p.isDirectory){ + val total = p.totalSpace + val free = p.freeSpace + val used = total - free + String.format("Total: %s, Used: %s, Free: %s, Usage: %.2f%%", + SizetoHuman(total), + SizetoHuman(used), + SizetoHuman(free), + (used.toDouble() / total * 100) + ) + } else throw Exception() + } catch (_ : Exception){ + "N/A" + } + } + fun getCPUUsage(cb : Consumer){ + CoroutineScope(Dispatchers.Default).launch { + val prev = processor.systemCpuLoadTicks + delay(1000) + val current = processor.systemCpuLoadTicks + + fun delta(t: CentralProcessor.TickType) = current[t.index] - prev[t.index] + + val idle = delta(CentralProcessor.TickType.IDLE) + delta(CentralProcessor.TickType.IOWAIT) + val busy = delta(CentralProcessor.TickType.USER) + delta(CentralProcessor.TickType.SYSTEM) + + delta(CentralProcessor.TickType.NICE) + delta(CentralProcessor.TickType.IRQ) + + delta(CentralProcessor.TickType.SOFTIRQ)+ delta(CentralProcessor.TickType.STEAL) + + val total = idle + busy + val usage = if (total > 0) { + (busy.toDouble() / total) * 100 + } else { + 0.0 + } + cb.accept(String.format("%.2f%%", usage)) + } + + } + + /** + * Get RAM usage using OSHI library. + * @return A string representing the total, used, and available memory in a human-readable format. + */ fun getMemoryUsage() : String{ val totalMemory = memory.total val availableMemory = memory.available diff --git a/src/database/LanguageLink.kt b/src/database/LanguageLink.kt new file mode 100644 index 0000000..552468e --- /dev/null +++ b/src/database/LanguageLink.kt @@ -0,0 +1,4 @@ +package database + +@Suppress("unused") +data class LanguageLink(val index: UInt, val TAG: String, val Language: String) diff --git a/src/database/MariaDB.kt b/src/database/MariaDB.kt index f312a37..816524b 100644 --- a/src/database/MariaDB.kt +++ b/src/database/MariaDB.kt @@ -1,5 +1,6 @@ package database +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext @@ -29,6 +30,8 @@ class MariaDB ( var connected : Boolean = false var SoundbankList : ArrayList = ArrayList() var MessagebankList : ArrayList = ArrayList() + var LanguageLinkList : ArrayList = ArrayList() + var SchedulebankList : ArrayList = ArrayList() companion object { fun ValidDate(date: String): Boolean { @@ -41,6 +44,22 @@ class MariaDB ( val regex = Regex("""^\d{2}:\d{2}:\d{2}$""") return regex.matches(time) } + + private val objectMapper = jacksonObjectMapper() + /** + * Convert SoundbankList, MessagebankList, LanguageLinkList, or SchedulebankList to a JSON String. + * @param list The ArrayList to convert to a String. + * @return A JSON String representation of the ArrayList. + */ + fun ArrayListtoString(list: ArrayList) : String{ + return try { + objectMapper.writeValueAsString(list.toArray()) + } catch (e: Exception) { + Logger.error("Error converting list to JSON: ${e.message}" as Any) + "[]" + } + + } } init { @@ -52,9 +71,9 @@ class MariaDB ( runBlocking { withContext(Dispatchers.IO){ Reload_Messagebank() - Logger.info { "Messagebank loaded" } Reload_Soundbank() - Logger.info { "Soundbank loaded" } + Reload_LanguageLink() + Reload_Schedulebank() } } @@ -63,6 +82,8 @@ class MariaDB ( Logger.info { "Loading MariaDB completed" } Logger.info { "Soundbank count: ${SoundbankList.size}" } Logger.info { "Messagebank count: ${MessagebankList.size}" } + Logger.info { "LanguageLink count: ${LanguageLinkList.size}" } + Logger.info { "Schedulebank count: ${SchedulebankList.size}" } } catch (e : Exception) { @@ -165,6 +186,54 @@ class MariaDB ( consumer.accept(logList) } + /** + * Reloads the ScheduleBank list from the database. + */ + private fun Reload_Schedulebank() { + SchedulebankList.clear() + try { + val statement = connection?.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM schedulebank") + while (resultSet?.next() == true) { + val schedulebank = ScheduleBank( + resultSet.getLong("index").toUInt(), + resultSet.getString("Description"), + resultSet.getString("Day"), + resultSet.getString("Time"), + resultSet.getString("Soundpath"), + resultSet.getInt("Repeat").toUByte(), + resultSet.getBoolean("Enable"), + resultSet.getString("BroadcastZones"), + resultSet.getString("Language") + ) + SchedulebankList.add(schedulebank) + } + } catch (e : Exception) { + Logger.error("Error fetching schedulebanks: ${e.message}" as Any) + } + } + + /** + * Reloads the language link list from the database. + */ + private fun Reload_LanguageLink() { + LanguageLinkList.clear() + try { + val statement = connection?.createStatement() + val resultSet = statement?.executeQuery("SELECT * FROM languagelink") + while (resultSet?.next() == true) { + val languageLink = LanguageLink( + resultSet.getLong("index").toUInt(), + resultSet.getString("TAG"), + resultSet.getString("Language") + ) + LanguageLinkList.add(languageLink) + } + } catch (e : Exception) { + Logger.error("Error fetching language links: ${e.message}" as Any) + } + } + /** * Reloads the soundbank list from the database. */ diff --git a/src/database/ScheduleBank.kt b/src/database/ScheduleBank.kt new file mode 100644 index 0000000..99d1269 --- /dev/null +++ b/src/database/ScheduleBank.kt @@ -0,0 +1,4 @@ +package database + +@Suppress("unused") +data class ScheduleBank(val index: UInt, val Description: String, val Day: String, val Time: String, val Soundpath: String, val Repeat: UByte, val Enable: Boolean, val BroadcastZones: String, val Language: String) diff --git a/src/web/WebApp.kt b/src/web/WebApp.kt index 87dc002..55684df 100644 --- a/src/web/WebApp.kt +++ b/src/web/WebApp.kt @@ -2,6 +2,7 @@ package web import codes.Somecodes import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import database.MariaDB import io.javalin.Javalin import io.javalin.apibuilder.ApiBuilder.before import io.javalin.apibuilder.ApiBuilder.get @@ -9,13 +10,13 @@ import io.javalin.apibuilder.ApiBuilder.path import io.javalin.apibuilder.ApiBuilder.post import io.javalin.apibuilder.ApiBuilder.ws import io.javalin.http.Context +import java.time.LocalDateTime @Suppress("unused") -class WebApp(val listenPort: Int, val userlist: List>) { +class WebApp(val listenPort: Int, val userlist: List>, val db: MariaDB) { var app : Javalin? = null - private val objectmapper = jacksonObjectMapper() - + val objectmapper = jacksonObjectMapper() fun Start() { app = Javalin.create { config -> @@ -55,35 +56,58 @@ class WebApp(val listenPort: Int, val userlist: List>) { path("home.html") { before { CheckUsers(it) } - ws("/ws") { it -> + ws("/ws") { ws -> // WebSocket endpoint for home - it.onClose { + ws.onClose { wsCloseContext -> // TODO Handle WebSocket close event - println("WebSocket closed: ${it.session.remoteAddress}") + println("WebSocket closed: ${wsCloseContext.session.remoteAddress}") } - it.onMessage { + ws.onMessage { wsMessageContext -> try{ - val cmd = objectmapper.readValue(it.message(), WebsocketCommand::class.java) + val cmd = objectmapper.readValue(wsMessageContext.message(), WebsocketCommand::class.java) when (cmd.command) { + "getSystemTime" ->{ + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, LocalDateTime.now().format(Somecodes.datetimeformat1)))) + } "getCPUStatus" ->{ - //TODO Get CPU status - - it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) + Somecodes.getCPUUsage { vv -> + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, vv))) + } } "getMemoryStatus" ->{ - // TODO Get Memory status - it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage()))) + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getMemoryUsage()))) } "getDiskStatus" ->{ - // TODO Get Disk status - it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, Somecodes.getDiskUsage()))) } "getNetworkStatus" ->{ // TODO Get Network status - it.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command,"OK"))) + } + "getSoundBankList" ->{ + println("getSoundBankList command received") + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SoundbankList)))) + } + "getMessageBankList"->{ + println("getMessageBankList command received") + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.MessagebankList))) ) + } + "getLanguageList"->{ + println("getLanguageList command received") + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.LanguageLinkList)))) + } + "getTimerList"->{ + println("getTimerList command received") + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply(cmd.command, MariaDB.ArrayListtoString(db.SchedulebankList)))) + } + "getLog" ->{ + println("getLog command received") + } + "getSetting" ->{ + println("getSetting command received") } else -> { - it.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}"))) + wsMessageContext.send(objectmapper.writeValueAsString(WebsocketReply("error", "Unknown command: ${cmd.command}"))) } } } catch (e: Exception){ @@ -91,30 +115,37 @@ class WebApp(val listenPort: Int, val userlist: List>) { } } - it.onConnect { + ws.onConnect { wsConnectContext -> // TODO Handle WebSocket connect event - println("WebSocket connected: ${it.session.remoteAddress}") + println("WebSocket connected: ${wsConnectContext.session.remoteAddress}") } } } path("soundbank.html") { before {CheckUsers(it)} + + } path("messagebank.html") { before { CheckUsers(it) } + } path("language.html") { before { CheckUsers(it) } + } path("log.html") { before { CheckUsers(it) } + } path("setting.html") { before { CheckUsers(it) } + } path("timer.html") { before { CheckUsers(it) } + } } }.start(listenPort) @@ -122,19 +153,13 @@ class WebApp(val listenPort: Int, val userlist: List>) { } fun CheckUsers(ctx: Context){ - println("Checking user session at ${ctx.req().requestURI}") val user = ctx.sessionAttribute("user") if (user == null) { - println("User not logged in, redirecting to login page") ctx.redirect("login.html") } - println("User is logged in: $user") val foundUser = userlist.find { it.first == user } if (foundUser==null) { - println("User not found in user list, redirecting to login page") ctx.redirect("login.html") - } else { - println("User found: $user") } }