From 6dda3844282d17d228f1f4e73377bad22d42b5a2 Mon Sep 17 00:00:00 2001 From: localhorst Date: Fri, 5 Dec 2025 21:21:18 +0100 Subject: [PATCH 1/5] add drive type to api and data model --- layouter.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/layouter.py b/layouter.py index 57aeace..b719642 100644 --- a/layouter.py +++ b/layouter.py @@ -34,6 +34,7 @@ logging.basicConfig( class DriveData: drive_index: int drive_state: str + drive_connection_type: str modelfamily: str modelname: str capacity: int @@ -48,6 +49,7 @@ class DriveData: @dataclasses.dataclass class DriveDataJson: state: str + connection_type: str fam: str name: str cap: int @@ -61,6 +63,7 @@ class DriveDataJson: @dataclasses.dataclass class DriveDataPrintable: + connectiontype: str modelfamily: str modelname: str capacity: str @@ -132,6 +135,7 @@ def cut_string(max_length, data, direction="end"): def format_to_printable(drive): return DriveDataPrintable( + drive.drive_connection_type, cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelfamily), "end"), cut_string(20, re.sub(r"[^a-zA-Z0-9. ]", "", drive.modelname), "end"), cut_string(20, human_readable_capacity(drive.capacity), "end"), @@ -306,6 +310,7 @@ def generate_image(drive, rehdd_info, output_file): drive_json = DriveDataJson( state=drive.drive_state, + connection_type=drive.drive_connection_type, fam=drive.modelfamily, name=drive.modelname, cap=drive.capacity, @@ -346,6 +351,7 @@ def main(): temp_drive = DriveData( drive_index=0, + drive_connection_type="sata", drive_state="shredded", modelfamily='Toshiba 2.5" HDD MK..65GSSX', modelname="TOSHIBA MK3265GSDX", From b3eaafbbe53a96af7404d0119686b0b7cbecebe9 Mon Sep 17 00:00:00 2001 From: localhorst Date: Fri, 5 Dec 2025 21:41:52 +0100 Subject: [PATCH 2/5] print checkbox --- layouter.py | 57 +++++++++++++++++++++++++++++++++++++++++++++------- output.png | Bin 4530 -> 4711 bytes 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/layouter.py b/layouter.py index b719642..a8d0583 100644 --- a/layouter.py +++ b/layouter.py @@ -49,7 +49,7 @@ class DriveData: @dataclasses.dataclass class DriveDataJson: state: str - connection_type: str + contype: str fam: str name: str cap: int @@ -153,17 +153,19 @@ def format_to_printable(drive): cut_string(30, str(datetime.timedelta(seconds=drive.shred_duration)), "end"), ) - def draw_text(drawable, printable_data, font_regular, font_bold, font_bold_bigger): """Draws formatted text with Cycles and Errors on one row.""" - + y_start = 4 line_height = 26 label_x = TEXT_X_OFFSET value_offset = 115 right_field_spacing = 200 # Horizontal gap between Cycles and Errors x_capacity = 520 y_capacity = 142 - y_start = 4 + x_connection_type = 600 + y_connection_type = y_start + y_spacing_connection_type = 25 + # Serial Number drawable.text((label_x, y_start), "Serial:", fill=0, font=font_bold) @@ -250,6 +252,49 @@ def draw_text(drawable, printable_data, font_regular, font_bold, font_bold_bigge font=font_bold_bigger, ) + if (printable_data.connectiontype == "sata"): + drawable.text( + (x_connection_type, y_connection_type), + "⬤ SATA", + fill=0, + font=font_regular, + ) + + drawable.text( + (x_connection_type, y_connection_type+y_spacing_connection_type), + "◯ NVME", + fill=0, + font=font_regular, + ) + elif (printable_data.connectiontype == "nvme"): + drawable.text( + (x_connection_type, y_connection_type), + "◯ SATA", + fill=0, + font=font_regular, + ) + + drawable.text( + (x_connection_type, y_connection_type+y_spacing_connection_type), + "⬤ NVME", + fill=0, + font=font_regular, + ) + else: + drawable.text( + (x_connection_type, y_connection_type), + "◯ SATA", + fill=0, + font=font_regular, + ) + + drawable.text( + (x_connection_type, y_connection_type+y_spacing_connection_type), + "◯ NVME", + fill=0, + font=font_regular, + ) + def draw_qr_code(image, data): """ @@ -274,7 +319,6 @@ def draw_qr_code(image, data): region = (5, 5, 5 + QR_CODE_SIZE, 5 + QR_CODE_SIZE) image.paste(qr_img, box=region) - def draw_outline(drawable, margin, width, output_width, output_height): """ Draws a rectangular outline on the drawable canvas. @@ -303,14 +347,13 @@ def draw_outline(drawable, margin, width, output_width, output_height): for line in lines: drawable.line(line, fill=0, width=width) - def generate_image(drive, rehdd_info, output_file): """Generates an image containing drive data and a QR code.""" try: drive_json = DriveDataJson( state=drive.drive_state, - connection_type=drive.drive_connection_type, + contype=drive.drive_connection_type, fam=drive.modelfamily, name=drive.modelname, cap=drive.capacity, diff --git a/output.png b/output.png index 7f9d9dc9308fb55d22e15eeb4fef43c8b265cc95..6d01057d63f570356c282e481838b386208d367f 100644 GIT binary patch literal 4711 zcmV-t5}56YP)R1ZL$R{&yt&Jb~Q7%53@@y z$=y{bOMrv=0OaibIRE+IJ9q9m_uM=1=Os$UxgW44^?slNU*g_-T&W_BFsoQr6Wp8) zq0Pd2PG}9;r}KK=y2MCHmwMhT=sB}sC9R|dKv{|?xD#KyE~6~C-jskSiYNlidq^`b z^>G3pWz}thE0bHY0+LQ=N?i8OI;Z$SFsOV;pm{ScQA`&PSJ6WUnQp?@6ree^IX*;@0RCx`0AnUWrEM~}C8 zF#%o%zzEHnHanm$-=QBAdY=(Ougx;(C7lIs@(tu#%Z&|c?-$dAtxfvDamOIE=v~j@ zp!*EwjY2U(48-l$JRqu3`^$B{S&yR^7)<<%-%h-zfBLtdd0CGbueN(J0V1Z4UjhtK zWhmCU1C(r}*@Ul3R^YoeISb1G7W6U;68!27MNKW&$B#gTyghnt={Gy&w>zFa&=XL6 zh`hbdAs%N73kv``0TAuN!2|>-9vIFVYTzebv}WV1PoDy6`JQS&WxXNnKn&W0_{OPt zd>se9Qp<+$p|$1$9dGqq7PtRvuI+83*m1699$gm?m>qh zf$HTeJz?njd@Dr_HjJbiQOK#FTN^S(R`*G#5B{L-f~Kmp0;JnNQz{*8f#w6PG{!{| z!DZ+K;{S>Z0RjhDf&h;IlCD|pEHfT5>;0g~nQqw8F|QTQ$boXBTQwq36@&HWeSUjU zHmkpMoLbzJlmDBy6HCUe31@M3^u~qdbjsPP-dOspKQwJU^Yimfh_#hl#g0t1Od7w8YcP9iH3qEjen%{?!4KxtVsuudST2Q3-|AhD~=U}IiC-$Du|@cVDzy;eK9ne@Qot=#a}CzKboKN zaR)sr{*8{YS;N7^*NPRsp}X@49ZxU)_tnY#ba}93?%iwHP&n`z|L``rcBMy>;(a=` zHzEBK1w20Yn>DIbhjd6nk00ye*HV3%v z^1Vv&n!B?mE?_A0xH0Z>+u~d9ZL_oW_47w|PZ*)$aW)}DH~X3eyhTIAF1$*ORr`ojOQ1$>x!5%Zx;DTwgLKNg+wf4 zd0-*5IbFO2gnLSd35y%BEnH0#Zo8IjI?l}MjRBN@wH&a`VbnGuBc%+Y zUESJ}oN%=6mpbO8*IqOKUjEbP-;!L@Ydrr}W%^DP)A~(@ir3QRCS^jn z8CnzTe51rR42HIDI_}7~`D?IIHu@z^Me(+QTO&G}&(Li4&7b@s`S6<~(%T)$wxi#^ zJFptLe||}*Jn`JjNhf?b_40Riy_Nd-N$s+A>K@?UA5qcVS5NL0*R&nKdf}JhvBj^A zfBYn;?*Z=pk%qWN9vCCE0T?oJ=*9B#5VI^4ii@X1GqhFI(9GH6$llA-I^-R#?e)H%jOs0QU61>NB$=*^=9_c>~C|pG= zGJaY&bqi%kHRw;(qt)cOh2{b|N&gLRZ z*YS^|^AI_K|B-Okbc8Jc$tfVRBQTg&iNK7+cK8~m|F*{2nwsOG!Giq_)E5ySi(+U; zkgyxfcuo4D&^eub$X!b+#hNmSTXc_67@2RNtfGy6CyYkEC2k!Niq1<<9qPEOq(KUR zg_>pGP0c4YbqGLoirK`#2xxDx)sJY(cTcUw z206W)JW=@R(r1H;g9?(z3gK@rJ?-XX85qfERO0Eh@&y0d|3p9(eMdrTT` zUEGFZ91b}us;LFJ1E?&ncAd8M(zv0XKJi##Y+lq+Zi1^DyM=96Q_dw7V87x3Z9{_g z-7Sh{7^H<)Pdg)*a>ISkUC}z`CffR=!P3=ykWFsGX{alA_8;z2bi!`y@8A9R@1H#N zzz2IDJ>Vq5C{p%(ZruCDmyDd)d!uDA(nv1#Y0kis_esXjePR3eKKeWEtJa&^Hv{Rl z|EJrMw0_6*>uvV{_x^|qldGi@C#9a!mrcrrm7cG*^6Z@z?~Hq2M1}r7t!3#<>GG$q zwEa4hdVD&ym~R7fthQRtoG$1xBLE8aB^KkQ&ol+Q{sQDr=6i8%?d!Qy!mVa{)i}c4 z;bz9)N-kDbvdMwhssZG;SQ9eUP z-J}bE>%des$~_6oJ{Os{1%U9Ju;>Hr^Rza=tuF;}#f87_tH~KYtozF#1oMJ(H4ivf5p(U!npGrmay zz$Njsuj(z={Gs4m2k*>23 zzm&Nw%xbZct*M)jfy+Y5oi*9I$yT}3dod7?vuNm|n5<-#)`BR?MbSu4Ma$%d6TQl@ zGI@LI%Ja(A*+R!J0gjo$#$Z=A4e3c%xR_XNceFBq@1Ra@g>A-n-|l7PgVog2@udKv zIV~s&OnAE7F@CNtXO%FQdH$A=fpYZ)?{Lp~sXsiB9RRTGv#6E(h8&bOK-vKgfoW^4 za1hg~zvbR=Kg$EbOAN)S7J3onAPFfCTKPF1^wQ!WhwAeE+){s2A`={Uo^w*Upfu+f zeAJO zl+w*eDU)jiIvGqkQc5W|Zld|wZSr;9+LVM~*%4c-)w697?_Y#&Pfng!&WY6{$6MQP zb$49{W}FT6hJq7rp97mWx*Bb&HRL;H)utj)TMyK!wj~&T2V6F6EU+yWcK%W%z@0Yk9|vKJrW&Qzh_??5)@-3s^V7xS{9zuKQ>+ z+$Q79?e=85A4fyV5P7l1D2QE&8(v~l|JT8NFxfjA95~k2I%Y0DeYU-5^}IX)5EP8) zF-Na9D`8b9aLbrJUVA2CdwdlHOk*Dh@F?r)K){+KRMwgjR$+PXjRVS*_+uj7+)(lW&v0R&Dk)4XY9M<>oEXiuEfu+@FIBsQ(K;P?+RCe5`f}V)-Of$#o7vO zl?ZM_+)yNsJTPpW>-^}*3h$mRzxP$`$O;^HN&2c=+33x@Lg3D>Y4wiP9QoK5UwUlY zFqM4ia5W=*reFgUYrD&X55nt`al%lyM&+0LxrOBu49q;7! zz_xa>>ftt$IPiJzaksUTwN)|!UMh2=o7C0RoQ#ONmgdA4*=|Z9Z_J3q@F&`6+jBj# zV=ijV7QVoIgZ9F3=0YFs$)#)AE7^qXB(?Vo%lAG$w^#Z!hnB1&U3Rq$mgLgqS8`H1 zy%{N`%*X9=vzPspla)y~D@VOlln64?k5V!pCAZlR_J)+}T=YE%aUBLB3IZ3u3?D`H zf)7;U1fiYE?Yw2aKKFdjS8f(N?9ci(pI1(~ZzbMH3Y?TH|5^(%8kT^{As(*iW=O|+A~>RJmcMXceWGx zsT-DWeLW2Y+QmPmak%2PI&s_x0ZaleWh$Vu?(R@Cdu&?_K6BLKjAzD1!s&G4ABh1x z=(iwUKd8|J7smxEHa;OD8^(8s7E#8;uvw(C-J~{fR8@H4gXedCaC|({POQ-LmF~i-Q7S1@$CIt2JI&eBV~3V%JnH=y%}T2k28tmt!Wd2qmU$ zSOmQJ$Oa{FfXG{67V;)20vHeNf?@!fAS)-Ll$0ee(MFGK&y<4HUK!EbJrLn3(&^jP zopn+}UP5Fe#YsucO$Cadng{}I&IuA8ij+=LMr5UoVvnNnyrbZNq8R;HaWfBA&h5a6 z*}t|;xm#_oJpoMR?6BSJyz&f0yhdthi0Ur165(ud5SJ7l=kNK zE#{P)^Cczt%}M2)F}-}P(cS7vgg{iH&R#Y1^V^HW1~*;{AnADVIi?FUL1{R(I*}Xh z30k^Zl1tmO^CBCOlpa|rouoYPCEesbvYQc=E^tix!K{3L(J%FcH~W5R=Lb{Ae!pY& zofDng1vaQyE~~OtaBuhqCb$CD&Embl6~eb52Q^#jDrCK%*(hhaA4{zEDrc=*pB@q` zjUeiYtQ`;nz%%a!Usc7ksXg=KR=&T;ue$!|bu<4RZFWeGmkXA&E#OuVwJVi^Z8her zi1-?tjJ(ice1Vc#{UoABin!~O#85mmRDhK1wZpFm**%A!mP-SVXWUc726FARq&pRX zm?YkX*(4D;h&C=88t-3twW*YQ3+#3jZ_eFPRiZ-8Hxl49d2YvRFV>Kbq@T@xP)t{d zO`q<#F>d}E=gwZ=`QmFGZ-%$uy?rhN5R4@s%Kd0zEEhgeaY(ix#-a$RgKw>6Bweow zUmHBf0Y}UMP>vv!R(f)4RcGxrMhsG!A4oab6YjoSyD#q@n9L?RZ}IgbmHKUcpLBB8 zX%{dV#*riMLAwrvf>B_|9$WJQnw=vBOdI4wt_2dmMFmZJqt1BPK)rHRVaOaQk6vME z;Jf3`l}=jMqw8|MQhF)AB#G=5d_cFhe#f^#+;!YP;qD;x_;W($`$j>*63yn{{zu`U0(LDyBVBQSYNcjgZ&`!R+{Nm5gSaH5^pjFLSt%uCJ;>ezJHLzW pnv|yr(8QPDVSkDHrEC@D{{S-JOFn0#A_4#a002ovPDHLkV1jmvn6xS0QZeE@q`*;1E6SmGE7?X=AdUM_w8%r-QzSq}fz(Va zR8&gphN4i?fE+W(iw#I73j=lEnm7ggPZpOHEy}1(vdEKCTiT-;kuDHvkGxcS)b36n z?v6Z)cPCL2(FE}Cfy?>r@AuvB&Cbqm<~K9&hb2nV-VE5hx*4dzm$?5=xMLOs7PG6G z!0fY5W+EALoKyLTjAV>#WX#B#F*9b~J+4(rDbwJ%qXNP(BnL3#B8|J}D`V?aJ~6u` zE-rYd?GwD<_b6BJHFwe>Tu-+;UqgUqmuox-TZu%{+W@?i!~g~jd}{-^Sdr|@dvzwt zO^XPQ0WKgw0-o3at}6qC@0V-HGJ)tAL`qOV2QVO8!CfU#)Vbv0@f_sG&&nU>znR&p zsE`d4bZJ-0Oy|J*0DZdw@KXTvz^KBl8efHu142Z~RXQCc(!h1Tjx1@VUeUj5J=o45 z1STD;8umK}Fh0-bLPQq;p5_8!W!$vebTX(rBdvwbL)z(v`na2RDd3e?F9tx!ME5wL zi_#AhtK9E~uS#0rIc1vJ1ppH|xC9cs(gOwSM1_nsV2$n;t-okj`#LpmVwsG!%AwHC zW@l#sBKSbqjEw;ZP+ZXM4e;%Zi~{9+Pc)t|-w~ECdaNEi{Y0UCbwk8i@2&zg=Rf<_ z+-2)IDaf^caD4_~V#he>)*9ne>VLsreGnKrbxQ6a0(%6in^{*Ak=qqTQ5aJpVy;n& zbi^Z(*#APq8BJBG2S_yDP`vg2)o_u7Z~vU60yxV)V=do&!sz#ua+<&}SHY%!XlCtCC|M#6o{&BYcv0wkkj#Yzvwm2hD3KwaXiZz>ljhpToyIu#lYB{0B zs0A!AQyW*iymRE;GatPa9k|@)s*F<8f)<-DaDjF=11MApN{o+_9z$P7sT{x#J;eBkiU^Q zKXE66YJ7bXDdN%7*XrYntn2bLI-+eW4-TW7#P{w#gLQHzT*bXtT+O)>pA@@~D(-=) zKC~}BivR5R%)NoF!QCWD`efx~zkFqW+EIqEO3(V6vzRx$(-0L-O*FXs?Q-($uyHp16;%1b?7OV|UYka?-6 z|I;PA(?5}SW8L0JW!zs(HTU-Ka&LX`WMah9o^-!9k=n7&P99911Qb3-%Q18^Vnj@o zK2;|buSBcHdkc(N#>fi*!t(GTO~5Hm=>2-DBCh5v_1-%Clbkns`K)t^tJZ9wT!GN` zy{p1r@e!5)BqxB-3PEQ=B?KcB8{w-P-Z_oaWtKyM&aCxy)aMbu5xKw$A!=3W@oLmV zwq+u{%~>fbxw3l_C+8d^dupbNvkLrq|A(*rs2<+jIlAkp_V%@y*a1=i%$802u4^9g z(We1Ki?|f+ItAK0R31lE<$GI-0Qd?(3X~buR%}DJpn-2tYSXvl1_Qs`{?Q}@^WS-R ziVvH-Ape6`hf25OPU$~k7p|uO#X*ix9BR4$yCJ@+Rk#-0nOaCk5h8#tLW|-Xi)8GI zxSHskPl|_p<+t-~4oBo8os;ujporLPN1t2rQHEdl0YnH6?P*^$js}|WTt;-KB5qaD zp9%P(D3xaA7N9t}YCdU2@m-C*$vY`uZIgSPG12sKqpfwuuGP2fy5A_YiE)UeoS!;$Fssvhdb|^4 z-+F^L_QsX1deVvZ=0Dn&J-+w=a5qO(xUu!K?$mn+=iL;2$wy4>#?tc*U<{U{{RLCymjXQ;1?TSc=B@ zIhv^0@c0ZsE&rltidU;6SkWyckalIWS=Et*5LjcDN ze{HfWoq)s$i=2-xHQL%TfM=tQtV6BI_o>ze%(>je>d~U#gX#pf`;At={tseGcn3b2@tLBYd5WGlV zp;Si)f-#6e+=Y66jtd>s*~q@ieD7B(=7CjsZppOH4%Uz79(E6n%_$$>8rFAi1UDxt z%3tc5CP8%!XRu;!8FG=th>tCkF}y5ERk?_PtGfj$Up z`+(qyEm>oUhixt>fsZ1r1Z0*W~l==^@(mB^G>)`L_xp=RyTmlawOpkSQ!W{Yc&b0aOA7~<8Ron zse!|xtw*olG$-P#F8U<^7C^Jtbl^$r-uSw70m+Mn_m+Jg-2W-RpK$I8SAjf$Vi)Hx zIU;lA1`WjwZdF|E`1Ui3qs-MM-bqH<>mR$^mMX5U8iQ5yJ%gX9r3T)C)+*e~m5`dUn#Xl+Mrw)ZFMMjr~ z8&aJw%B7Y>YD`Kg2R0{O6cV5JDz)-*_k&API&H&wr@aeJPWMX@?wXe(c^_49r+T(v9D5_5R_F&s2Er zY?M^pL32yf&XMJwRyqIN<#8A*#M@%}AtNU8=9s_;EU9NL1_O(TG?`_tDsE-5)|4Fk zyyt@Sa!%;Gjubg$xC~d zb8=exOP^m2vNv7{k1QoaFF!0_PycFz?``eoP*b3r01;Ew4NDRAD3Lm#TK}%1wQc>Z z6JCt|plwIbo~6gM_}0tgfVG$%isS{EbW>hXNaX}|xBcvYvCV3@Rm5eQbl{EP*b7DK zS;{-35g%qMFSZnLpZlfxUXz;hCXO_m9ccQ(o}W*4LY7AH4+(58I`wuTZ-jurV!4>$ z_)jP#QZ~X@!7}MN@I#ZAT%!;q>=qkx4sL;CDuhHWzLmvxfMSe~iqL|g*1#nCf-zUz zy7DckGC2lRg7z!l)>FLJx@4-UT=)^RieqlUpnAPmREVowt||)toUGw9bFBxbySTS=2}DL?4#x~&4)Tmz&}fHNA@^0-3>jToZNyw8n_bQpEwzT(6r zUi5W*ZQ7PgE{c?POopVT3=1QQ>ff<~4T_?tFf9P~!#<(tjpmAl2DHMO9l*oah1frOAc1z0pj~CH3!+6bynC+sX4NMNNr8gNkn<#VW83QeYI%UmQBt`|eSFCZC;<{ViMO_lhgEKq`|iMlQ3M19&ieeFGc; zt6}j$;0ocHFa~A4rV43yWb#P2_E1-pcVnmXyOj&Vs)A5aWO;)S0Iu;M_^K+d1t(WE zPVxLjvlT>}7bh~$IlFsX?RcB%ZV0#@M0t;!wagm(l||uN2pJg>fuULQ#`+@>>BT_yZmDIMUbSZbVw#dh}(|tI0f9fUFPzsw!H^mICJcn`RT)3 z+bzH!jBOkH@$BGOu(NnjY8_%h6d@zvnJbEzuYAUc z9*X?~ar;p6vw?o)XXcV}%W!txc-tx3H*{WW;k4Z-U?eDnboXJa0)wocrOz5H{UqJ1 z{aqLq$Zd`Z60c4LO?#(8dssugcvYd#I7j^3qAt-r)%j<`1PlTzV#{2TdiRXF(yuouatsF6x>>T2PK4{TsMI^X%hb!yaiX>W+zn?>BVBw z?k;|GSyQjEZfZOQN15ZO1?0`KWtiNXx7y70wyTdSb`rJv-_%!OzXDb{K|N6%{*+5u zzh1WjoxI Date: Fri, 5 Dec 2025 22:17:05 +0100 Subject: [PATCH 3/5] ipc api --- dummy_receiver.py | 1 + reHDDPrinter.py | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/dummy_receiver.py b/dummy_receiver.py index bfcd21d..0a00581 100644 --- a/dummy_receiver.py +++ b/dummy_receiver.py @@ -39,6 +39,7 @@ try: driveData.add('utf-8', 'driveModelFamiliy', length=str_buffer_size) driveData.add('utf-8', 'driveModelModel', length=str_buffer_size) driveData.add('utf-8', 'driveSerialnumber', length=str_buffer_size) + #driveData.add('utf-8', 'driveConnectionType', length=str_buffer_size) driveData.add('utf-8', 'driveReHddVersion', length=str_buffer_size) # Dictionary representation diff --git a/reHDDPrinter.py b/reHDDPrinter.py index d9f18c9..e80582a 100644 --- a/reHDDPrinter.py +++ b/reHDDPrinter.py @@ -46,6 +46,7 @@ class TDriveData(ctypes.Structure): ("caDriveModelFamily", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveModelName", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveSerialnumber", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveConnectionType", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveReHddVersion", ctypes.c_char * STR_BUFFER_SIZE), ] @@ -101,6 +102,7 @@ def create_drive_objects(drive_info): modelname=drive_info["driveModelName"], capacity=int(drive_info["driveCapacity"]), serialnumber=drive_info["driveSerialnumber"], + drive_connection_type=drive_info["driveConnectionType"], power_on_hours=int(drive_info["driveHours"]), power_cycle=int(drive_info["driveCycles"]), smart_error_count=int(drive_info["driveErrors"]), @@ -132,6 +134,7 @@ def worker(queue_id, test_mode=False): "driveModelFamily": "modelFamily", "driveModelName": "modelName", "driveSerialnumber": "serial", + "driveConnectionType": "sata", "driveReHddVersion": "V1.1.2", } else: @@ -165,6 +168,7 @@ def worker(queue_id, test_mode=False): "driveModelFamily": d.caDriveModelFamily.decode().strip("\x00"), "driveModelName": d.caDriveModelName.decode().strip("\x00"), "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), + "driveConnectionType": d.caDriveConnectionType.decode().strip("\x00"), "driveReHddVersion": d.caDriveReHddVersion.decode().strip("\x00"), } time.sleep(3) From b85ff216488d67811512de526cb4374b0bc8df74 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 6 Dec 2025 23:16:33 +0100 Subject: [PATCH 4/5] add connection_type to dummies --- dummy_receiver.py | 7 +++++-- dummy_sender/main.cpp | 18 +----------------- dummy_sender/main.h | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) diff --git a/dummy_receiver.py b/dummy_receiver.py index 0ca3bdc..2975b9b 100644 --- a/dummy_receiver.py +++ b/dummy_receiver.py @@ -18,7 +18,6 @@ IPC_CREAT = 0o1000 terminate = False - class TDriveData(ctypes.Structure): _fields_ = [ ("caDriveIndex", ctypes.c_char * STR_BUFFER_SIZE), @@ -29,6 +28,7 @@ class TDriveData(ctypes.Structure): ("caDriveShredDuration", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveCapacity", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveState", ctypes.c_char * STR_BUFFER_SIZE), + ("caDriveConnectionType", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveModelFamily", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveModelName", ctypes.c_char * STR_BUFFER_SIZE), ("caDriveSerialnumber", ctypes.c_char * STR_BUFFER_SIZE), @@ -75,6 +75,7 @@ def create_drive_objects(drive_info): smart_error_count=int(drive_info["driveErrors"]), shred_timestamp=int(drive_info["driveShredTimestamp"]), shred_duration=int(drive_info["driveShredDuration"]), + connection_type=drive_info["driveConnectionType"], ) rehdd_info = layouter.ReHddInfo( @@ -88,8 +89,8 @@ def create_drive_objects(drive_info): def worker(queue_id, test_mode=False): try: while not terminate: + time.sleep(3) if test_mode: - time.sleep(3) drive_info = { "driveIndex": "42", "driveHours": 44, @@ -99,6 +100,7 @@ def worker(queue_id, test_mode=False): "driveShredDuration": 0, "driveCapacity": 42, "driveState": "shredded", + "driveConnectionType": "sata", "driveModelFamily": "modelFamily", "driveModelName": "modelName", "driveSerialnumber": "serial", @@ -145,6 +147,7 @@ def worker(queue_id, test_mode=False): ), "driveCapacity": int(d.caDriveCapacity.decode().strip("\x00")), "driveState": d.caDriveState.decode().strip("\x00"), + "driveConnectionType": d.caDriveConnectionType.decode().strip("\x00"), "driveModelFamily": d.caDriveModelFamily.decode().strip("\x00"), "driveModelName": d.caDriveModelName.decode().strip("\x00"), "driveSerialnumber": d.caDriveSerialnumber.decode().strip("\x00"), diff --git a/dummy_sender/main.cpp b/dummy_sender/main.cpp index ea63cb1..5cff79d 100644 --- a/dummy_sender/main.cpp +++ b/dummy_sender/main.cpp @@ -43,23 +43,7 @@ int main(void) sprintf(msgQueueData.driveData.caDriveErrors, "%i", 1); sprintf(msgQueueData.driveData.caDriveShredTimestamp, "%li", 71718LU); sprintf(msgQueueData.driveData.caDriveShredDuration, "%li", 81718LU); - /* - switch (drive->connectionType) - { - case Drive::USB: - strcpy(msgQueueData.driveData.caDriveConnectionType, "usb"); - break; - case Drive::SATA: - strcpy(msgQueueData.driveData.caDriveConnectionType, "sata"); - break; - case Drive::NVME: - strcpy(msgQueueData.driveData.caDriveConnectionType, "nvme"); - break; - case Drive::UNKNOWN: - default: - strcpy(msgQueueData.driveData.caDriveConnectionType, "na"); - } - */ + strcpy(msgQueueData.driveData.caDriveConnectionType, "sata"); sprintf(msgQueueData.driveData.caDriveReHddVersion, REHDD_VERSION); std::cout << "Sending message to queue..." << std::endl; diff --git a/dummy_sender/main.h b/dummy_sender/main.h index 2ed16cd..35445cf 100644 --- a/dummy_sender/main.h +++ b/dummy_sender/main.h @@ -28,7 +28,7 @@ typedef struct char caDriveShredDuration[STR_BUFFER_SIZE]; char caDriveCapacity[STR_BUFFER_SIZE]; char caDriveState[STR_BUFFER_SIZE]; - // char caDriveConnectionType[STR_BUFFER_SIZE]; + char caDriveConnectionType[STR_BUFFER_SIZE]; char caDriveModelFamily[STR_BUFFER_SIZE]; char caDriveModelName[STR_BUFFER_SIZE]; char caDriveSerialnumber[STR_BUFFER_SIZE]; From 731a00a644c93aa32cfb4a47bad0fc736d31f608 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 6 Dec 2025 23:29:05 +0100 Subject: [PATCH 5/5] reHDDPrinter reads and handle conn type --- output.png | Bin 4711 -> 4698 bytes reHDDPrinter.py | 12 +++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/output.png b/output.png index 6d01057d63f570356c282e481838b386208d367f..58a30f11a1093f12dbd8c37acc77ac79440153f8 100644 GIT binary patch literal 4698 zcmV-g5~b~lP)>&D2Ik zrKD~s2@ze$HUq!Nh#V2fL;F-9e*inSW9^ZINTZQ>K~SJh9D9#uMWU#zUFurxQM)^R zxIa9ScPG&n0SEB!;dW={H{bov?Ci{M=Jz}J{SqZ>Zv|{w-3nCTGu*q6D^;W(<`mOx zgPXS?w3uJb3C&UKWHDAWCple?$ym|wVtK7Xe~|& z#<$O%Nso+On49_yMQfEqWt`2=&jZ8=fT$ZA0}!CNpxaeQ2>hgj)^42j$1YY>>vAG> z!hBs=ff%-i@%0mx@ihdos+NTt?yNT#82;L;9p2}@lG=5+JW}5Ar2@e8{ga?uYo2vl z{T1}-W5D?7)AD{|&?`{gq5yEwW3fVlk_Kx=QiCYuRfw5uGDR-tlgS)>rt`d}sw4n1 zsq0F$qfMNQ4}-(p9Mbk+M0(Ddkyn%DAm_J@Ho`i$KP6C#lb~%tN^ziXG0ru)av@1 zYA|lraHfX)#Ho?(!s3G`v)S`{qUUn@+rACRm^WsOg?+m#iesKI3dPW@0mwDWa8-OP zP>+Us8@^G*Kl$}?`Q5@rA7|L5YTv}r*VnKCUn^DlhN3S^_nQk`&M!~g`p(i`+ik{c*lRM+>mOA-~-EvEG}BW5``vgSM>?SQ{8#-=V@=p(OrREx;FTE zp6mx=6Lk^*V?|&-v^ZIs1j75vj}w;GV4I+6!bxdKzqMV0YE&N#fbu=lUcnrD4LH!h z0tr&ZCfgnfW(+=j+Av?G#oeR-1n-%^X*RND=uP9m%Vi^rzZBi$_0~4_Jd~`}$?sG46z2 z=Lb6P2JV(fOI*DOj1gJ@jOuw}rIn>oW|{X&3nxP(v{cm4$Xny%&2jIY61|6B=%!FU zAmsC(->V!sn%SsmWg`Fqz(OwYOAet7dTwbqG6~lnX|*E-n(J<2PevI za>$=(j(enYVCK|699|qu6=TQUuFPznt)T?&<6>tDXw;W`OH3SNIsJ;Uo@^J7N0#n4v`)B9|*f%M_2-o zoB$##0-b4<2#iQRnS=B+QI9!30@D1}x8ajV6Q*QOu5p6T39r=C~ibNb^Zwd<;PJh}rny zY0zG$xgF7x@46!b;2wYhPztK8*oJOFCx1+BOs~h)j;gP28L#j#f$zW5j7 zC$-|VQ@ay(<$4-W9OML5qsjfhP4QK&%C$Pq)an`@A%H<5i|QMT742sAvqAjIKs4NY zqf0JcNh>e)khW7dAS{^ZmzmdTCwuDuAV&ppf^?!O)9s+jp@6DWz|ylqzcfd*g)r~ zAhoMQ(R7`RaGUmn^fOvVI_~-j7{2sud``N#?8cUTO4cE=L0X=_^mP2Cj{W4F{-iP& zm;R@huZniun^yMUy!M;Yk-;CmdCl2HDL#G|aJNRb61OLPEU&(t`atk4{boNq-&?Zo z3hq`&jPT6;#W(KB{^FjWfB)4BH}5N@{n9I+9Cr)9i};*lr!NgI+Gz$zUfsJG98si9 zo@pHCc5=$qqW+weQo7l^{7}wGDdm)OvrRWPcm7?xUElk<&PCzWGTqua!aBJ_-0b+9 z$%X1pHaYmi+D7e776&t}aEEqhhYqYF)w+0-d&lVxYAU&x-BuRUAA4z(=EHH zZX3SE+!uPEP^%6@c~GD-jTepsn4Z9RM;;WwQmIL06-{Ihy_oKGDWoq42ZHOb^(5aN z1aNG)SUcsOfF+-|DOdtPxOQ0bfmDG+bKDlNZ|u%#RYC0*gny` zx(XA^>?6BFvCB7v6w8^ezB2$k?}DW?=;}O9VL%|BLM&NfhYq+7(T0P+c6^fpfI`L3 zAT203l@e(_wt7Yr7n`=v0MtfHxKO=X`@?$PQU+-^$6e}7tD$}Kb5@))<>xvIA8Ddq ztmGPWixuD%A?1#S?3l56z6@5zSv2~#n5brz>xU@H+oDO88tymqBN3f^Xcx01y+RpW z@wxywW&~>wyK))GOt8$`@f#^yTLJKG)QJtKZTUXBYYBO8E%nvP#e>k87L)}Be08O3 z{Dr2RRl;-0nc36(LOGm@a%1N^E-$+2NdVJ2i&}kd$U$iVWNhFuFf1(r2QkbRF86FO zl>G1#{g6EuWP>4>_^A%A?oUHjrn2!Vk(biRN-2}|7tzCT z+LlsExq28Y!ZHl`v_69<~8Z8!|CO3Qd7!oEI(lc7EaYv+LQ!R?;4 zz1U~9q683wfxmt6kN)$U_k_Ir({%CQUv?x1fCu} zXvb>Jikr0w+|;Ls^omF9WAIfFFpV_?;Ib0S1On=Y(2CZUa18CCL%)6SuOCg`8vl>} zSKqtu2lLyEzk8-$uFnFn1e(2O0?)QEd~e6sWe~_-s=PPvD7gRKSe|j(!M2Hyq70ze zW0i~&v4uv3&guznOWe@;%Tr(bdjFvO#*>o+`r+A(xz~IOh8&W<>Qo)Q*>eQWY`xb0 zu?E-5rufopS-PPV%a7MG!Y4cnz_Z{B29qWL%j2zZHF4um{`}+%rf0I4H*Sq{`KS%1 zsm64~Sk1a!ie*-7878YIKTV~jpDb-pYc6%~-T>I-pc+?WLl%If+lp^^F6!QWSb6T9 zIj_I#Sg>vIw+ll(uqjVgGu&3P8Bf+`a5v@2+9+9hPLXzZq%BUJv!AvH7v192y1Dyf z$QGWTNtqSp^Zv7`y*}xFtt06q)-H{oD!wW#P3E8zCro<3d-K|@h4gFZHu^6<2JV)~ z*5QV`JBiQu=R1FW?)}Bi^G;!<*BOU=WxcH)Ka|C3FXn+!7L0`!V`U4qY%nidjPjOz ztu?77M!*%)UxMKhQuqY`CZZM*7AhS1$wcqSqujc7XwO@F4uA0**W#wNK3=&)?oTdU zx1;~*K>ni!12=5?6_-@-(I)w>2@WO2K~cSd1d51N44na`bw4ci7)2<_cgfd*iXPpqT5b`WgxYVFyH~T2Z+pzAkg*LVp6sUaG*ZGaV!MKB;4WPm9t!(& za`0mI{3FMlhgUp*o`(46imZ$5s=3d?j^?;6*G&~5_f$O{0h+TKxZt$1sO#0^D)!=R ziNq7h-@Wne)O#fL!n&Ag0f>prgth|e2+*)fNyTbV^|L(5ZiP$3mf%#=B=yteO81e&fneDefMo1ji&@u&$bk3SFhTIzkv z3 zV+1hOuf%E>uRH@0KO{Xm3b++Fri;rEJ`%wIHsmYuV|Mj8rs_j=P@%lr$;HYGlZoOa z`u2ARAK7h=w|ZJLi4chDsk2+lybJNaYs`2pfTZoBqMez0K%Vc|-r-%i@}mx|$D5m5 z+zhTxC6eC{xSA2F5+#l5|6e0%4^*MIzwy(!=Xh{hS$v&`0f6;ZiXmW-l^ z!T3C7qbN40mg>_EmB?nS3a%E9&z4dp?1gvY+x}oVdwdWo{~@SFOF9=L5EE3qFzX~D z57AmfqP73RZA_(|n_#6-+&O21szimxSVZhL?`^2@Vvfd;46xn~s?Zf;-CuW{3huy+ z*n4ePFd&0tBlC$Jv4Ke-7)$QVe|LT?AMUA!l5Rk(h$3VRe6yaBblf(4EpTlcY%vGG zN(7-aGZPzC?fPeo7^XTukhY8azIFNRRni$|pY6z$%e6;$_FwJctep}t5mru)+X&3osJ0-ze5F0d%ekcSVO&fRbkZl5#4)UR*a=z#d~)wdn?mN zYF@X=$|Xr;E#m_*bL0Q`)`+u;dk0Sk5v%;2kitN_3YL3YTpM_?miS-(mGW1b>bZ?a zCuRx2hZvd!PT>Nn{$ZFSn}szNmdA4E_+X3YFDJ!`$$Bb`#ldaIcT7SA%10)!AWhQ0 z1b@jD>-40GBK>4i+C$Zk&NJJ@L3_rd@Ei*~X8~n1zXCITWox&&QFrxG)k&h={5$Xf z98|z+B&cVq=1-$E%=ufR1ZL$R{&yt&Jb~Q7%53@@y z$=y{bOMrv=0OaibIRE+IJ9q9m_uM=1=Os$UxgW44^?slNU*g_-T&W_BFsoQr6Wp8) zq0Pd2PG}9;r}KK=y2MCHmwMhT=sB}sC9R|dKv{|?xD#KyE~6~C-jskSiYNlidq^`b z^>G3pWz}thE0bHY0+LQ=N?i8OI;Z$SFsOV;pm{ScQA`&PSJ6WUnQp?@6ree^IX*;@0RCx`0AnUWrEM~}C8 zF#%o%zzEHnHanm$-=QBAdY=(Ougx;(C7lIs@(tu#%Z&|c?-$dAtxfvDamOIE=v~j@ zp!*EwjY2U(48-l$JRqu3`^$B{S&yR^7)<<%-%h-zfBLtdd0CGbueN(J0V1Z4UjhtK zWhmCU1C(r}*@Ul3R^YoeISb1G7W6U;68!27MNKW&$B#gTyghnt={Gy&w>zFa&=XL6 zh`hbdAs%N73kv``0TAuN!2|>-9vIFVYTzebv}WV1PoDy6`JQS&WxXNnKn&W0_{OPt zd>se9Qp<+$p|$1$9dGqq7PtRvuI+83*m1699$gm?m>qh zf$HTeJz?njd@Dr_HjJbiQOK#FTN^S(R`*G#5B{L-f~Kmp0;JnNQz{*8f#w6PG{!{| z!DZ+K;{S>Z0RjhDf&h;IlCD|pEHfT5>;0g~nQqw8F|QTQ$boXBTQwq36@&HWeSUjU zHmkpMoLbzJlmDBy6HCUe31@M3^u~qdbjsPP-dOspKQwJU^Yimfh_#hl#g0t1Od7w8YcP9iH3qEjen%{?!4KxtVsuudST2Q3-|AhD~=U}IiC-$Du|@cVDzy;eK9ne@Qot=#a}CzKboKN zaR)sr{*8{YS;N7^*NPRsp}X@49ZxU)_tnY#ba}93?%iwHP&n`z|L``rcBMy>;(a=` zHzEBK1w20Yn>DIbhjd6nk00ye*HV3%v z^1Vv&n!B?mE?_A0xH0Z>+u~d9ZL_oW_47w|PZ*)$aW)}DH~X3eyhTIAF1$*ORr`ojOQ1$>x!5%Zx;DTwgLKNg+wf4 zd0-*5IbFO2gnLSd35y%BEnH0#Zo8IjI?l}MjRBN@wH&a`VbnGuBc%+Y zUESJ}oN%=6mpbO8*IqOKUjEbP-;!L@Ydrr}W%^DP)A~(@ir3QRCS^jn z8CnzTe51rR42HIDI_}7~`D?IIHu@z^Me(+QTO&G}&(Li4&7b@s`S6<~(%T)$wxi#^ zJFptLe||}*Jn`JjNhf?b_40Riy_Nd-N$s+A>K@?UA5qcVS5NL0*R&nKdf}JhvBj^A zfBYn;?*Z=pk%qWN9vCCE0T?oJ=*9B#5VI^4ii@X1GqhFI(9GH6$llA-I^-R#?e)H%jOs0QU61>NB$=*^=9_c>~C|pG= zGJaY&bqi%kHRw;(qt)cOh2{b|N&gLRZ z*YS^|^AI_K|B-Okbc8Jc$tfVRBQTg&iNK7+cK8~m|F*{2nwsOG!Giq_)E5ySi(+U; zkgyxfcuo4D&^eub$X!b+#hNmSTXc_67@2RNtfGy6CyYkEC2k!Niq1<<9qPEOq(KUR zg_>pGP0c4YbqGLoirK`#2xxDx)sJY(cTcUw z206W)JW=@R(r1H;g9?(z3gK@rJ?-XX85qfERO0Eh@&y0d|3p9(eMdrTT` zUEGFZ91b}us;LFJ1E?&ncAd8M(zv0XKJi##Y+lq+Zi1^DyM=96Q_dw7V87x3Z9{_g z-7Sh{7^H<)Pdg)*a>ISkUC}z`CffR=!P3=ykWFsGX{alA_8;z2bi!`y@8A9R@1H#N zzz2IDJ>Vq5C{p%(ZruCDmyDd)d!uDA(nv1#Y0kis_esXjePR3eKKeWEtJa&^Hv{Rl z|EJrMw0_6*>uvV{_x^|qldGi@C#9a!mrcrrm7cG*^6Z@z?~Hq2M1}r7t!3#<>GG$q zwEa4hdVD&ym~R7fthQRtoG$1xBLE8aB^KkQ&ol+Q{sQDr=6i8%?d!Qy!mVa{)i}c4 z;bz9)N-kDbvdMwhssZG;SQ9eUP z-J}bE>%des$~_6oJ{Os{1%U9Ju;>Hr^Rza=tuF;}#f87_tH~KYtozF#1oMJ(H4ivf5p(U!npGrmay zz$Njsuj(z={Gs4m2k*>23 zzm&Nw%xbZct*M)jfy+Y5oi*9I$yT}3dod7?vuNm|n5<-#)`BR?MbSu4Ma$%d6TQl@ zGI@LI%Ja(A*+R!J0gjo$#$Z=A4e3c%xR_XNceFBq@1Ra@g>A-n-|l7PgVog2@udKv zIV~s&OnAE7F@CNtXO%FQdH$A=fpYZ)?{Lp~sXsiB9RRTGv#6E(h8&bOK-vKgfoW^4 za1hg~zvbR=Kg$EbOAN)S7J3onAPFfCTKPF1^wQ!WhwAeE+){s2A`={Uo^w*Upfu+f zeAJO zl+w*eDU)jiIvGqkQc5W|Zld|wZSr;9+LVM~*%4c-)w697?_Y#&Pfng!&WY6{$6MQP zb$49{W}FT6hJq7rp97mWx*Bb&HRL;H)utj)TMyK!wj~&T2V6F6EU+yWcK%W%z@0Yk9|vKJrW&Qzh_??5)@-3s^V7xS{9zuKQ>+ z+$Q79?e=85A4fyV5P7l1D2QE&8(v~l|JT8NFxfjA95~k2I%Y0DeYU-5^}IX)5EP8) zF-Na9D`8b9aLbrJUVA2CdwdlHOk*Dh@F?r)K){+KRMwgjR$+PXjRVS*_+uj7+)(lW&v0R&Dk)4XY9M<>oEXiuEfu+@FIBsQ(K;P?+RCe5`f}V)-Of$#o7vO zl?ZM_+)yNsJTPpW>-^}*3h$mRzxP$`$O;^HN&2c=+33x@Lg3D>Y4wiP9QoK5UwUlY zFqM4ia5W=*reFgUYrD&X55nt`al%lyM&+0LxrOBu49q;7! zz_xa>>ftt$IPiJzaksUTwN)|!UMh2=o7C0RoQ#ONmgdA4*=|Z9Z_J3q@F&`6+jBj# zV=ijV7QVoIgZ9F3=0YFs$)#)AE7^qXB(?Vo%lAG$w^#Z!hnB1&U3Rq$mgLgqS8`H1 zy%{N`%*X9=vzPspla)y~D@VOlln64?k5V!pCAZlR_J)+}T=YE%aUBLB3IZ3u3?D`H zf)7;U1fiYE?Yw2aKKFdjS8f(N?9ci(pI1(~ZzbMH3Y?TH|5^(%8kT^{As(*iW=O|+A~>RJmcMXceWGx zsT-DWeLW2Y+QmPmak%2PI&s_x0ZaleWh$Vu?(R@Cdu&?_K6BLKjAzD1!s&G4ABh1x z=(iwUKd8|J7smxEHa;OD8^(8s7E#8;uvw(C-J~{fR8@H4gXedCaC|({POQ-LmF~i-Q7S1@$CIt2JI&eBV~3V%JnH=y%}T2k28tmt!Wd2qmU$ zSOmQJ$Oa{FfXG{67V;)20vHeNf?@!fAS)-Ll$0ee(MFGK&y<4HUK!EbJrLn3(&^jP zopn+}UP5Fe#YsucO$Cadng{}I&IuA8ij+=LMr5UoVvnNnyrbZNq8R;HaWfBA&h5a6 z*}t|;xm#_oJpoMR?6BSJyz&f0yhdthi0Ur165(ud5SJ7l=kNK zE#{P)^Cczt%}M2)F}-}P(cS7vgg{iH&R#Y1^V^HW1~*;{AnADVIi?FUL1{R(I*}Xh z30k^Zl1tmO^CBCOlpa|rouoYPCEesbvYQc=E^tix!K{3L(J%FcH~W5R=Lb{Ae!pY& zofDng1vaQyE~~OtaBuhqCb$CD&Embl6~eb52Q^#jDrCK%*(hhaA4{zEDrc=*pB@q` zjUeiYtQ`;nz%%a!Usc7ksXg=KR=&T;ue$!|bu<4RZFWeGmkXA&E#OuVwJVi^Z8her zi1-?tjJ(ice1Vc#{UoABin!~O#85mmRDhK1wZpFm**%A!mP-SVXWUc726FARq&pRX zm?YkX*(4D;h&C=88t-3twW*YQ3+#3jZ_eFPRiZ-8Hxl49d2YvRFV>Kbq@T@xP)t{d zO`q<#F>d}E=gwZ=`QmFGZ-%$uy?rhN5R4@s%Kd0zEEhgeaY(ix#-a$RgKw>6Bweow zUmHBf0Y}UMP>vv!R(f)4RcGxrMhsG!A4oab6YjoSyD#q@n9L?RZ}IgbmHKUcpLBB8 zX%{dV#*riMLAwrvf>B_|9$WJQnw=vBOdI4wt_2dmMFmZJqt1BPK)rHRVaOaQk6vME z;Jf3`l}=jMqw8|MQhF)AB#G=5d_cFhe#f^#+;!YP;qD;x_;W($`$j>*63yn{{zu`U0(LDyBVBQSYNcjgZ&`!R+{Nm5gSaH5^pjFLSt%uCJ;>ezJHLzW pnv|yr(8QPDVSkDHrEC@D{{S-JOFn0#A_4#a002ovPDHLkV1jm