From 44fc93eb4e670db6053b8b8b93fed37d3b7e5514 Mon Sep 17 00:00:00 2001 From: yuntang <123@qq.com> Date: Fri, 26 Dec 2025 17:29:18 +0800 Subject: [PATCH] 2025-12-26T17:29:18 --- .../sample_data/new_2025_12_26_10_25_03.xlsx | Bin 0 -> 7012 bytes .../sample_data/new_2025_12_26_10_25_31.xlsx | Bin 0 -> 5437 bytes .../sample_data/new_2025_12_26_10_26_11.xlsx | Bin 0 -> 5710 bytes .../sample_data/new_2025_12_26_10_26_45.xlsx | Bin 0 -> 5712 bytes .../sample_data/new_2025_12_26_10_54_03.xlsx | Bin 0 -> 10901 bytes .../sample_data/new_2025_12_26_10_59_00.xlsx | Bin 0 -> 14415 bytes .../sample_data/new_2025_12_26_11_22_19.xlsx | Bin 0 -> 7167 bytes .../sample_data/new_2025_12_26_16_34_45.xlsx | Bin 0 -> 6102 bytes .../sample_data/new_2025_12_26_16_35_51.xlsx | Bin 0 -> 6180 bytes .../sample_data/new_2025_12_26_17_25_52.xlsx | Bin 0 -> 6107 bytes .../13-4-new.xlsx | Bin .../peak.xlsx | Bin src/.vscode/settings.json | 33 +- src/DSCAnalysisTool.pro | 2 +- src/global.h | 6 +- src/logger/logger.h | 4 +- src/main.cpp | 3 +- src/serialport/dataparser.cpp | 283 +++++---- src/serialport/dataparser.h | 5 + src/serialport/protocol.h | 57 +- src/serialport/serialport.cpp | 507 ++++++++++------- src/ui/experimentsettingform.cpp | 536 +++++++++++------- src/ui/leftwidget.cpp | 472 +++++++++++---- src/ui/leftwidget.h | 40 +- src/ui/printpreviewform.cpp | 1 - 25 files changed, 1266 insertions(+), 683 deletions(-) create mode 100644 experiment_data/sample_data/new_2025_12_26_10_25_03.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_10_25_31.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_10_26_11.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_10_26_45.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_10_54_03.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_10_59_00.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_11_22_19.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_16_34_45.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_16_35_51.xlsx create mode 100644 experiment_data/sample_data/new_2025_12_26_17_25_52.xlsx rename experiment_data/{analysis_state => 新建文件夹1112}/13-4-new.xlsx (100%) rename experiment_data/{analysis_state => 新建文件夹==}/peak.xlsx (100%) diff --git a/experiment_data/sample_data/new_2025_12_26_10_25_03.xlsx b/experiment_data/sample_data/new_2025_12_26_10_25_03.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..6d7e1cdb5b8ee5aec8d63e63a7acedbc282df39f GIT binary patch literal 7012 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 Ze~MX65e@zKkAQdYpt~M8^!pMG@ISLeSF`{C literal 0 HcmV?d00001 diff --git a/experiment_data/sample_data/new_2025_12_26_10_25_31.xlsx b/experiment_data/sample_data/new_2025_12_26_10_25_31.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..600bcfc896af6db723ccfce90ec3bd86463208d8 GIT binary patch literal 5437 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 BRWSen literal 0 HcmV?d00001 diff --git a/experiment_data/sample_data/new_2025_12_26_10_26_11.xlsx b/experiment_data/sample_data/new_2025_12_26_10_26_11.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0aaee9ad86bd3decaf28bf6ef997809d54f65847 GIT binary patch literal 5710 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 b0Kk7KW_4vu6rBJ75cLG0w9)X(>jM4{|4ZD# literal 0 HcmV?d00001 diff --git a/experiment_data/sample_data/new_2025_12_26_10_54_03.xlsx b/experiment_data/sample_data/new_2025_12_26_10_54_03.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ee3579c0a90e4b91dc3c5888324f08ba6e793626 GIT binary patch literal 10901 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 G`u0Bv41Fa4 literal 0 HcmV?d00001 diff --git a/experiment_data/sample_data/new_2025_12_26_16_34_45.xlsx b/experiment_data/sample_data/new_2025_12_26_16_34_45.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..554d17805f7bc5ed0ff0875388d2f2389be4b2ec GIT binary patch literal 6102 zcmZ`-bySq^x*chTZUIRN>4rf%r9(g(q`Mo5p}R}ERggwnQhMm_?(UMf=d%w@~?foc7L&IPL00;NPRzz{i2e>n0f{KU?X%yh zQ>W^uHj~WQj9*{sKU7;BBdBy&_0D>*eEsU+y9OSaxQF0LV70pykRWkF_?cS?9?4+-Ws-6fbtu&Bm7O@Utid~o)wA@%ytZj_2Jqn~77`Ar z?FYN_26}GVUivZ!1$SMDRuOqopNYG#<0cX<7#U1=gX>hXl+uvCMmYKUIptaC9JV8? ztb({c&n-G1y&&?(_XJ%+7JMcT1SSw(?rY=s_xlX@s&%y5z9IV9?K30+)S!hO^Ksj1 ziR39;18x&deT9B6!}?I(!frK~wCijG){G?Wr%15z^T>t)mra;ab0^r4^t!B`%x~pS zFnzdoJ!|p{j}fnLICbH=o_SS95T8w@>LaTchG+}?N;{tJb$Rs8B=?4!u)#Iyu41xO zc2eN2CJl1W^~kE%h=32uInt-HusqSP6x+KvoO*yx6hK|m2WVRk@S#~}&a;iYXDDNU zIpkN4TiW)(n-K zH{3V$48ihCCSgzcU9wR)=g?zy!Ei$KTpQ853NCOl+SEMe=h1nh=BtEzG{YuuWd4|d z^?`~8sNkTR^;$1(=VpeVMq^CR!G|QgS_bruQ0`hLg63o>alIofy0Dz=E#G2=SBSt? zq9{X?V_`n@?%9KF0s>)+_wWjTHTXx3I!B&o000&g0D$(V!S!uyEPnU3B1G;7Gy3;a zoQVZ9VkvSBj$(hnFp_Z2Hxn!yg0GQ8)2L<-8BT=??yKL}cISut-0S$OLOpYxMz%0x z)sc~%jQq!fF!^hkF40=jHZ^RG*b{BV;djUFnJ=!4`eKIbohjKDT*6-2tN1avu#IMa zN}8EU{E%~jUN=CFS0Z#)96ADG%t@3+!3zkuiwZ2TP+yKKxWTPz4pvj1i*uXo9%waJ zmkwxtiy`7GvMtH}xR^I!QXzFq6LCbjwoYzIx$L#+naPuz zl&WNBXfymcBl%*^iAKSBMw+2dK{#btZxBn`{{4^O%ePm71Y*1o`;9rg>`ne|itR$Y zH@W-^Hia@#%luGsTdBhH(cW#&WQbD{65;`7#Vdt5?AhUdW{K3NcKQ{K^*^8e&2v2k zKEviCzjcp16a2%ouDy|^!|!qWZ?+kK{ZkyKU~E zw`v(nNmDMX+e;ioSt0~7_{{e3sbu(uu<`Q4b}lM&KUD=BN`y12sR=G%lk@H-H-i3t z@qzCfSZK_m#4!0i_Du|XD~cF4UK-oJV{Iu`)DN;^!CR;DL~l#EA!FTN`gOQonHXJD zje?*{*g86CB?vWrrcf|QdQhF_32qTKhH^IvaZaf7__##}vR`v_0~v99MwM{5an`Ip zPRBE+nH-a>Q4DTq2PVfcXC+a-`EPnU zgg*(nJYGZe$9E0-BRz&T1`75zwhl~swzj|SVR~$<+%7Y^*rkWq8lTf!vNL6F@h@Pt zBhh?ZHA0gSHBj|BeokU(m*jXf6kSC66neyn{$#ez`J1$a*?RORd$IerU0EQnrR1RFOk*E&lDByEsHRTWj7quj}G>14$| z=X$r``;vd~R2r2BN>#{-xKq$sLd)@Na;SGV11*#5XY@&4Eov)4@vFPsTN}r-EsI`j z7lNXt(QFAJi=o5eA)O6hdFk?r-ZoZPh=w{$Ycs(Lv2t@o0xL%Tmg%q$6$u+ViJ6&0 zVk50T@n^&IP;qX3_}T2|-%unJo35?`Q2^OX1`g3mhsvo)>d1iuhCt6v^`O z4rBbMNCr0cM*pY;v2OdN(p_lx&vr@IFBP$*RXB9f8VQA#+8+R4k436yqnk^yNWt&N zjT{Hl=J4x2?i&%^`8o-I#Wa!(C0CGf$gZZmQR`oQgUv0E^SSDsLJsbW6ct!De>0@g zcZ6j+;4t4e+-));&I^RT#oX+Xtv;J+L$ZHYjsq^2>sU>Cam}nZ+897g7cX#36 z^4&g}5y=_6tO<|&x<59-qXqkAwj50L?2QbS9PQ1lO&oqv>=Xx)f@Z-EyyWjdpX3}3 zC1+)0gh?tXED7SQhL$x>%hPej4FGB!R;?7%y3(o9jcJ&-y2Ma9y=(G~skb9=>3PUx z+7uvTp-Qw9*A(S<($e=e6@@1tN95X$jz=_JQ4%o_% z_hmO&0Qs}Wh);dOm=qCxeOeM_MOEVkd^`BYIrbBVJ_y>No*!7gkDf(2N3TcqJQ9jQ zIxT=cWe>5Og;W!i*h+5~fB2n4w44e!KJYW4n-4!)9q!i7n~qWUS}5`mx89|y^g?oq z%2ZO;W8%>7Hqv+K2b5d@j@Nw)g!&<)HHh^Ga6 zodijIWw`Y_3#q*RbcJfevt7qp?>q`Xjjx3XWo|mMV`l!d*9=h!@q*~@p>uHr@6ss9 zO7oWW*3HdWlt4D1rHLfTFO6&D?NqsO`|gd>$f}9PX3O+2u??M2?$4ps&Ff;;+BkBV zw7yoVviFP_cArbPNadf8)#r+sS&E&#_Zgcd`9mUS*+T@SRr#9yme-IkznG+lq`1I}$q<`w8ezWBv@Y^X zrEis!{7ARq-qtLz_`J}SN`#MY=O0-dO^vLKn128MPBHuHvQeYr&zmk)!#&`;Bwtj}RhY)fW(5R{ zTW76+%Be4+%ezvN_hL0+yQC5dM?$6Z3zLWot0hRQ-fowwONkY<>u&8vQLOg6P6rgv z0PQ7sS2yqOk9Y<;q+MPHUW7!)cM(Cjbt}Hl#DG~V*XkmoQ>Im~vD$9sOx$!6*01H-Aa4UEl9ze zMXP!7@H`#185zunM(^~ltY2t$d%(>+>()NOrF3H=#pruybGfhXu5-hI2I8XHN-Euz zbMg3GQPNRA<%0HjbHns*m6WSXh)wYlJl=#xKPx|UKzL^yjw8XKm?3U7i-~IZ5ogQn zDU4cSDp*1aKM7E8X z@+IvbONf!{dlfG2Cw`5Q6=7@0CNi$Eb;acSwyhtWY4pib&M%v zbV2bIN9-TyqnNO$&&&W9Dr!(VnR>=;PLk7ZH8AV|tfP}NImyXJ1sNO;Kxv>NVr@V7 zuDu@nra7%Nmk9y9@v?s5sx!+1SI+?vqLp`wAe9mpL`Oo({D3)_6IDV6Tk7x_kqoY4 zHa_vTUY4(wl7!ANHHYz&coOoqL~=;U5W)&88x2A>PRCG4xZa|1<(F{m zP&t`yi;oUObsf}==x;(4O$dTsUx;w~V0V}X(dA-(*CN{{SOKs0+AijZrk51ijF7TZ zOSqfRhL&jWG-E_WS#*XAb9pU>Zyw3IvL{Qem?D+5-Jua4;%0R6z}Vk;E_yDyEOs<$ zsz9jst08a}yAF1pOocCEyW-3FI1JWO`ZOwG^6#Em8i$3Ovcyr7Y2mI&)e0`{5g?ZE zf1;g3vV+o3lMyCGn(z@tOtQr+qjfXsk_2{|d^os8Mlda^uFJIHI;&jvm8}NmF@A|* z1r?Na2Gn3}thHcCIcQa~U#gi^Z^(Xu^B;|C9xdF=Av0ip>9up2*2+t7LQIaYpNY+4 zA07ybz^X64b1yX;nIN#C-lq^6>ENJiuQNf8$uMeu_H zHc1ic)^G;QjIN(*Kf8xMvKB2BH^#BsTRns;%srhL!q;y>ylbPGSCSS z;d-5n8&Q4AWUfbo!hjzY@|o5-5wb#@NlShq`~ZlUQr^=aYDhLolBeWl+c^yY1FdjF zR7-AifRRl(G=WsFzs3q4tPFj@MRD4jg*9J={Sg2nFkLRd zH=0Og>YBhLh`1sS|}heLVTus{{uUEdg7#ual7t)JRJ=VB#Acz$un;JacN$#*ryetkk| z*qHt!Z{Zc*;BZTYnULbQJ&!#U-a``Hnyk?GjvzYk!YqH2etOl$%ZUTL`3BjsPio#A^MN4(nnRLtX(o5*o2<_@yUIO_ zmQRxH72sHkkQ!0BN0oLLt*DGzpn@d<6pYdi^*b> z8u#x*m7Z33>dXIBV1FDGzrxo4y7Z^er{3`o6oUL4`qW82E%08PHAa}knUDOy1Pq|mR4F?KtM|3jsEU^UU~1% ztTmrCvu2+^_Bo&Z-TSD>!@}VL07ysxpU@0FDZ|>i8(08f7##p00002q_N-n`u6AzL z78dSqtiKO-7HT<0=uK;{ne5PT-5tp)&vXFC13Yk?`w9FxL&20$15X z`w2;oq{Kiuo14#b2;(?!PLBh4uIQ9z8wDVo-dKxyyiB9B7bo1~_cv$DRV>6BJGa#% zUSIPGtRBFc7Zx0?NyTKd@94&1GXhHn7q|U{I?*F$UDXfI_TLKNA=Z|U&Ik$5tj;V~ z^W{YQd1TkGqNyTre%@IY_cZesyhD|4F?32DXxHyNe%*I1<;viBo!(x>y=<1ODD|Fs zF(j*}R6DM&>ZDa95skL+U^>w0wLG{3qfF7~`o5FV+JBnV{Z2haIArfqlb|4~FCu-l zQs~Zws65)^zLP(Ym9-1J+%%TnsKR46cIGBTm_0HYG#sq27oJ)bkA{S=f0q_Hb@B>N zdpgb;jewX<%GkPFYniLjBtwQuTU#@<{;cq z+IV!=EK?pQJ$zWgcp}o2_`H9JTY+2Z4B!C~#BxPTemx~15o;k5(Z@p~v~8T9M5Fhkw|4I^xJ{dVKbx>zuqL?W1%Cx+Gj(}^4Fa_f*TuHu=iOwn%v5(RG z` zFhGau@GKSGHKb~c&R+23q1fv#g6t@_!;4KvtP>^vVl+^W7+Pk^k`5v|9cl*RTI~LJvgv!+gSCbS=gZtm zTNWzJ$+>)v&wy3$q2TD>kXCmMiv@W!XpY8Z$xY9_8hi@GP0$iP;KC1nq0yl+1D^6t!aqB`rrQJ-iFY$rBo)pLnF)~tN zHWeFX1zh=%S2iiMXD+6-9j!lM{^q%rija8+%5MvlXQF?2HgdJFcl)i*|7M%z*Dn=NW*RwUp z{X8_bue4Oa)JW$v3v)c6PS1m#=SZgCDvm;=;bF1MQ(h|{a_wNbI#9jg6rgwh_}xH` z1O26(-S8Dxm{f)~)R$UdNZd?C6;+MX=j zAqJn)770F=7|-W>HVk6H`!J&peh%JpNLx%|%d+}Twna6uZ4jPHPZ$}&a(o7KnY-LK zqOI`a%m3m!GQ|CkU^$TObblj7x$AFw2E^WpdO^n!6Z+`FgwkW~WTxWk*5+;eN?4FmH6%M54`i%u<#hA# z=V)wRYT!N~FZf<*KEPx}$oq|PvyuAc(&y|4Gr|Pdyn!NI6sWc676$KiD%$FVxT%Wz~RzjW%O`OT2^8>X+JxB*dJZ1FV&*vvc z4stMZd49&97B!>y5LM9K72Z0zpYPg@IC>G4ug&Dkh}umaPfZ$bhbYU}%#HMNz>n(c z!1Z(yZIEho)h2Ubz1_8*3Z^0BW)PfUmgUQa|^GQYlN zQeJi58Om8Ra?;812malp(id^+Y|tjzLuVN4KbvIc5Z1F0fhc90!}_c(>>P?Sop;h`I{o+Ymx(wV3+PlHF60_W zO-pk7eZ>_~`G=WvBDGZMSjai9`}BKZZwOz8bUQKLONsqRH4S`mPr_N2>N&NxT(n(M(Z!QBX}Xqi=H=!q8w(mih2 zs9^AB)MosoYun@XhQ{MXXULmY7b33@54o&6!W8T@DK1ia5<(vOrXki6h(wggJbUp; z$d()G;$|X+A8BOyH?hq_z!E*77AhlLve>pETxKi0p+z=aFu5chw?C7pp#jZ~ z|4behB#hTVxOE9+WHGuHjXlO|^=hcPkX@j$mR0ndJAQQ=8#37kR0;zJ+;@-CKIC-2 z#}~DDKV$p7!z?T&MR1Fs0BifkRwK!=Uq>f+@O&YXbhgm{X@~s?-IpDnfSS^7Uz~{9 zwPuPk)>Fq>b=2^%=8vc~;y3Q0f>$-2cym@?Q0vsO)78C>=Bj+!N}}6TvEt?nuDkV` zKzdWr_@}i3+RR=}XSQNs{jCdXYPpy^4J{Rh0#7>I;^?Kl51rF1nGP?uHZTBRcWMwi zA1S@DAI*|Iw2e=oZAAN58{K{EEq?d!ZNlg;ahJTj1LgZ~QcX_cjq2b6!y0iD#G0b1 zmw|#1F7%64FLQoR)G(IT=~;nseZgseJFXTPvZOkQ;|n{vqOok1M)UIn_vXMNDnL_+ zofUOqHmZB>TNpm(xa1@eOk>zWu*h8&6-8Ciy2)2t8+LVGC*HNWRN0b_Ey{kH!o+XB z9rCDJDV8?tOmGQpgFs&bSRLDzH(R~W3R(5v*J*Kmm^L5&A>S?c_Q$8zx8mgTcIf0C zf@|TBn@_X5s~hwP3PzXG9(z7bi$b5d&QZn%`B_iq92GJ0nPzJN_9tcK8=y)hQ|xX+ zT(R%dI+agPm)Jr5+iC}9?aRleJ&q`*#BOXS3e87YF!OD%{W;*cy!En}^9qGDcH*sNpT25$}RMSRUrM*3lb9{iA8a@lmW{M)ir z(y~Sw@&>iNDjhk=(te}egE*?qG4I8&iY1V%jKJp3-Tevw_<+3E)9{O^_@tpHqtA`% zFrx&IW_WkCc_Lo9IiSdfnUQglCdNK{I^A0Ty+^XWQVbglZmULr=n10+gHDsfUJrN5 zj*pmWGtV|jLse&2G057nTXV7dP>-;d)$jI;K-lqPVM;PNYfcn$%F41=;9Ivo&jET3O!g zF2Z7Q9wXANtY9HdkG1G7YK9$tBi`-7^9N1+uGQlo+3;PcL4p`e9xtlLM3?u*eQfh? zU6Z}4w&yY|+6TL8LW~YP+m3XRSG6}X8LvDmW-m%*dMAP`BW+KTFQamKhD{Gf1FRPchE*$ z1_-qWKAfy5BlI%zqPzE|uKM54&*$a|lLelg_M}?2fBN3_aJ5&r+Q|l+jP7?oTPU0( z;Cp{khrcS!*3AP?x_3em6DcwO0cPnd)`BIbh}4EVZXf0hHat3v4d6mk8)%qoV%h5< zyXeyd#|6MSIXzdBo$pXl06zm%g{vYrk3BzdHR0N^Wsv8wB0{uWH!a)rWMAR=aP$P( zAuvOPMjdC=P)5%7h%J&ET}A;{?)W=01w!p|Qp#vLzU6L20GIU5E6_}72TfTGTFpv;F8^oG?zExArS=XL8LijANmmXbFdyrtEHVNE#OTirNvZu)gxm)bUs+lnO! zp)-kHGi5sk6lQEs1NJYodIXrPNGSSA>q6+IIR_TzEw8Ub3{(G->5{V2c9wZ zw^(7mU6M;WoOLyK@d-m0VQf0m9qyAIBI=`pPgMopnlA;hp=`u{E$vO3$2(JfHD;Vw7pmAtA*_LjVs!T1wxqlWG1aiQO(yF{I0L69|(3ip1QT0nI+uGGktS|l$ zi?DxNYNfS%AO4POd=mHa%QNaReF8n=0MEP-b`j&GZR?>8u`rTW6>K#pa%1OV^I$`y z5lT(un)5KXGR8%ti;67im8}q!Z_@x|P_|N7Nc9C4<1uaYC@aWqzf?=_=MD zt$o@HC&q?VvGw*$+}L4f=~tGSWp=oz_kD@Vb*87!0|c}|wmFt~HnxgJFLBE|vO&du zpXSE!=D*+w+v4#0<(;Dt@qkJ7#+$@DQKqC{M`4Rn=GftXZCW4+9(NdPPMQ_wmtq#5 ztc<+WLC?*oBL>Uw(GAB)BqrVH>ryBub-Kar?br+DqgDk0j}x!iQUjX#-x{ zi|s%{0NG2~*6%!j%ls|FQ`TQOOCIV`QK08PA?shkN>v_~9W`k64orRAwn7s*+}aUY z&GY6O)-k>8h?pd&M{@aGM^-nK~ zv>aX*o+%@caL$es4)Zs`H3(C+hK?ERFn_7@N5lOp@!E(m8PY<_&Zc~meRkC=zzqi7 z)Q|4FrgrUo3Wj?EvK>vwqo3br>U8{}H&o+azJ8kSqO^M>oGM&{H?EBiBE0lvl2pVcdn;ieURaHUC7@LnPbBH5piY>a zQ%XPFW_WD4zlJRE^^Ppy;zp(MA!p5|0xhC*TG%6R)(9aBloYX@UM5J}-lRF~H zh^U1znVv(A(`Bb!b@%!@dklHaOQx<0(^4y!`&hEDT=>;$l&a_Q*QOPW+OyT|_%9FU z-e=J53Jeec```Dm(2@N83JU&p{I~7w<0_B0H2>590D^+qpnd&Um49y$9)lmR9{zyS zpneDn{x6G&$8{cO^gnf)5dLpse~f+{9{-^Ak^Tey?8ZIe`wTW;N$E22Z#xsvH#~X WtH{Gcn*jiTpzk242F8MaefuBv(Z)>x literal 0 HcmV?d00001 diff --git a/experiment_data/sample_data/new_2025_12_26_17_25_52.xlsx b/experiment_data/sample_data/new_2025_12_26_17_25_52.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..8f27fde60c1b7fdb0a7555a6cf9b3de54c3cc0c0 GIT binary patch literal 6107 zcmZ`-byU>tx*cYy89D}}LqZw^9FT4pTHvEWI)@>KZlnbX5k%=w;R7Tj1SzGYq*E!8 zmKs{(j(+D{kKB7^)|z+yX084FvG@Btzr7zFbr3cs0DzAVxcl&#sejxj;wlgTphN@! z+yDRoey#$(NKY3pM>{)|m%y(>h~E$4nrNtrEL0_J-gzzinG<#Bt5R3!u{FGf5PkMV z!sY8!JP}p0-^x5vv}-7hg|0}oVe|0s0CNL#+oOX`RKi?M^>-Ewn4O4GRybx8k z{7$mUZP8zc_r_mbH}m_-o}r(a7Y*k_Q+xrsB7Z z^j*C)WN3!8MGwW?kjFKNxvZBM%v<4k)zL~?FJy8tE z-;uc?LZj&pqEA!Xw5xPsMGwI^Bh9;21CAIiM)Z^URov;=qC5in~dTCFn7 zKM%MS1N__ofZd&#u@eB0%ugl@ji~x&U`;y6;v=xVdIjZh8uFsk$nv6Uo>_o5>0GTV zYjm-)5(nn&nznH6>tCkO3L{lt-3&bOxyi=`X1kmm@2|Sc3e3 z9&98|nuXzAkTq0Nqxg6J-{w= z-v;msK*QDswkgKt*oG&%RQrk7iMO|U>U7nEW{L$u5P2Qp;umAuGAOJw}yUneU zNS9yTt=p&dL6Gzv8agrO#0Y0K5iO4d3=_x~ym6pF(*FI3VVc&-*l@^HPCp>UU z=R|v0S2s8Q!1|NtMm8G0RSdsxF+9`$!?UHQovYWcq55yO`G5XXp4#q=pi8PPe67>U(=qbW##-K{pB4CJY96!!8CzFpTaQ+sEVxnQrt zwv*R^M(jJ!*E;sVna|l*HZ-Z8PpaqSJ|2Z_!Q%}Muvlr@cyYvdjTI=VvI^bu48+uJ zGB|3J$_wR31BA@*(<|1xNe?qGhO8^b!ycxS>eN@d*2a`j+n>i%8upi0$MGeI5 zwB@%h3eSVRv5%J!ItorM?UEWDV9=Qz#-!J-<-}33JeEK98dip8mnD5~!ulBtAYIn|pCDJ%2 z82G11ace|Df*6rpF?X2kKSi=ZdfNS?QlH!VpDJC%_x$LHBYRVw%3KTONj7;>>S`Vc z_sZgVuIJmbPzc{AHWJ6Dqu#O^M9WEpqHRFqq$Ak0L(kzDZ@T z<1HN!qh?>0X>Z~Km;-MEbGu5pN?-!nKJ{Mk|uQzSv!8QZ^TA|6+tA!rTa~e{H+XLnvXtJZ$E;f zt%ezc_~ayElP997OcS47WEBxojg~7hGQX=kg4@^o@@@cPNH_v1b| zNQg4}Sh|yR^3G^H>uq6v?2L-iikLffAWi$MA`2g?D2T~U?NT|nACD2wn2B?{ZwiO^ zUo82k)la9#y$54<>9wH zm3y>9)#zLM`bcex!%!X8mK!$lPX

}WKeT0hxdto*qDz_NxE??4< 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 C#*U%@ literal 0 HcmV?d00001 diff --git a/experiment_data/analysis_state/13-4-new.xlsx b/experiment_data/新建文件夹1112/13-4-new.xlsx similarity index 100% rename from experiment_data/analysis_state/13-4-new.xlsx rename to experiment_data/新建文件夹1112/13-4-new.xlsx diff --git a/experiment_data/analysis_state/peak.xlsx b/experiment_data/新建文件夹==/peak.xlsx similarity index 100% rename from experiment_data/analysis_state/peak.xlsx rename to experiment_data/新建文件夹==/peak.xlsx diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 96d4ce0..102de47 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -5,6 +5,37 @@ "string": "cpp", "string_view": "cpp", "deque": "cpp", - "vector": "cpp" + "vector": "cpp", + "list": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "initializer_list": "cpp", + "cmath": "cpp", + "complex": "cpp", + "atomic": "cpp", + "*.tcc": "cpp", + "chrono": "cpp", + "bitset": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "memory": "cpp", + "optional": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "functional": "cpp", + "fstream": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "mutex": "cpp", + "new": "cpp", + "ostream": "cpp", + "ratio": "cpp", + "sstream": "cpp", + "streambuf": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/src/DSCAnalysisTool.pro b/src/DSCAnalysisTool.pro index c41f6a6..ff5ad81 100644 --- a/src/DSCAnalysisTool.pro +++ b/src/DSCAnalysisTool.pro @@ -9,7 +9,7 @@ CONFIG+=precompile_header PRECOMPILED_HEADER=stable.h # -VERSION = 1.4.0 +VERSION = 1.3.9 # 设置目标文件名,包含版本号 TARGET = DSCAnalysisTool_$${VERSION} diff --git a/src/global.h b/src/global.h index dff003a..6891466 100644 --- a/src/global.h +++ b/src/global.h @@ -96,7 +96,10 @@ namespace Global { QVector phaseVtr; ExperimentInfo() - : sampleName("new"), sampleWeight("0"), date("20250101"), experimentor("experimentor") {} + : sampleName("new"), + sampleWeight("0"), + date("20250101"), + experimentor("experimentor") {} }; struct PhaseTotalInfo { @@ -179,6 +182,7 @@ extern QString _smoothnessFileName; extern QStringList _fileList; void updateFileList(); bool isFileExist(const QString &fileName); + }; // namespace Global diff --git a/src/logger/logger.h b/src/logger/logger.h index 2a76c2e..1e9a999 100644 --- a/src/logger/logger.h +++ b/src/logger/logger.h @@ -1,7 +1,7 @@ #ifndef LOGGER_H #define LOGGER_H -#include "easylogging++.h" +#include "../thirdparty/easylogging/easylogging++.h" #define logde LOG(DEBUG) #define loger LOG(ERROR) @@ -17,4 +17,4 @@ public: static Logger *instance(); }; -#endif // LOGGER_H +#endif // LOGGER_H \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 09da339..2b50525 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,7 +15,7 @@ int main(int argc, char *argv[]) { - // system("chcp 65001"); + // system("chcp 65001"); QTextCodec *codec = QTextCodec::codecForName("UTF-8"); QTextCodec::setCodecForLocale(codec); @@ -27,6 +27,7 @@ int main(int argc, char *argv[]) // 启用高DPI支持 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // 启用高DPI缩放 + QApplication a(argc, argv); logde<<"version:"<(charArray[0]) == 0xA5 && + // 判断前两个字节是否为 0xA5 和 0x5A (小端格式) + if (static_cast(charArray[0]) == 0xA5 && static_cast(charArray[1]) == 0x5A) - { - // std::cout << "前两个字节是 0x5AA5 (小端)" << std::endl; - } - else - { - // std::cout << "前两个字节不是 0x5AA5 (小端)" << std::endl; - qDebug() << "header failed."; - return false; + { + // std::cout << "前两个字节是 0x5AA5 (小端)" << std::endl; + } + else + { + // std::cout << "前两个字节不是 0x5AA5 (小端)" << std::endl; + qDebug() << "header failed."; + return false; + } + + if (static_cast(charArray[3]) != 0x83) + { + qDebug() << "mark failed."; + return false; + } + + memcpy(&cd, charArray + 6, sizeof(CommonData)); + + return true; } - if (static_cast(charArray[3]) != 0x83) + QByteArray setExperimentInfo(const QVector &vtr, const QByteArray initBa) { - qDebug() << "mark failed."; - return false; + // const int phaseLength = sizeof(Phase); + const int phaseLength = PHASE_BYTE_SIZE; + + const int phaseArrayLength = vtr.size() * phaseLength; + char phaseArray[300] = {}; + int offset = 0; + + for (const Phase &phase : vtr) + { + memcpy(phaseArray + offset, &phase, sizeof(Phase)); + offset += phaseLength; + } + + // int totalDataLength = 0; + char totalData[300] = {}; + + u16 header = 0x5aa5; + memcpy(totalData, (char *)&header, sizeof(u16)); + + // command + addr + data + crc + totalData[2] = 1 + 2 + phaseArrayLength + 2 + 2; + + totalData[3] = 0x82; + + u16 address = 0x0050; + memcpy(totalData + 4, (char *)&address, sizeof(u16)); + + memcpy(totalData + 6, phaseArray, phaseArrayLength); + + memcpy(totalData + 6 + phaseArrayLength, initBa.data(), 2); + + // + char data[300] = {}; + + data[0] = 0x82; + + // u16 address = 0x0050; + memcpy(data + 1, (char *)&address, sizeof(u16)); + + memcpy(data + 3, phaseArray, phaseArrayLength); + // initial data + memcpy(data + 3 + phaseArrayLength, initBa.data(), 2); + + u16 crc = modbusCRC16((u8 *)data, 3 + phaseArrayLength + 2); + // + memcpy(totalData + 6 + phaseArrayLength + 2, (char *)&crc, sizeof(u16)); + + // header + phase data + initial data + crc. + return QByteArray(totalData, 6 + phaseArrayLength + 2 + 2); } - memcpy(&cd, charArray + 6, sizeof(CommonData)); - - return true; -} - - - -QByteArray setExperimentInfo(const QVector &vtr,const QByteArray initBa) -{ - // const int phaseLength = sizeof(Phase); - const int phaseLength = PHASE_BYTE_SIZE; - - const int phaseArrayLength = vtr.size() * phaseLength; - char phaseArray[300] = {}; - int offset = 0; - - for (const Phase &phase : vtr) + QByteArray setDeviceStartStop(const DeviceStartMode mode) { - memcpy(phaseArray + offset, &phase, sizeof(Phase)); - offset += phaseLength; - } - - // - // int totalDataLength = 0; - char totalData[300] = {}; - - u16 header = 0x5aa5; - memcpy(totalData, (char *)&header, sizeof(u16)); - - // command + addr + data + crc - totalData[2] = 1 + 2 + phaseArrayLength + 2 + 2; - - totalData[3] = 0x82; - - u16 address = 0x0050; - memcpy(totalData + 4, (char *)&address, sizeof(u16)); - - memcpy(totalData + 6, phaseArray, phaseArrayLength); - - memcpy(totalData + 6 + phaseArrayLength,initBa.data(),2); - - // - char data[300] = {}; - - data[0] = 0x82; - - // u16 address = 0x0050; - memcpy(data + 1, (char *)&address, sizeof(u16)); - - memcpy(data + 3, phaseArray, phaseArrayLength); - // initial data - memcpy(data + 3 + phaseArrayLength,initBa.data(),2); - - u16 crc = modbusCRC16((u8 *)data, 3 + phaseArrayLength + 2); - // - memcpy(totalData + 6 + phaseArrayLength + 2, (char *)&crc, sizeof(u16)); - - // header + phase data + initial data + crc. - return QByteArray(totalData, 6 + phaseArrayLength + 2 + 2); -} - -QByteArray setDeviceStartStop(const DeviceStartMode mode) -{ #if 1 - SerialPortProtocol spp; - spp.head = FRANE_HEAD; - spp.len = 1 + 2 + 1 + 2; - spp.cmd = WRITE_CMD; - spp.addr = 0x002c; - spp.data_buf[0] = mode; + SerialPortProtocol spp; + spp.head = FRANE_HEAD; + spp.len = 1 + 2 + 1 + 2; + spp.cmd = WRITE_CMD; + spp.addr = 0x002c; + spp.data_buf[0] = mode; - int sppValidLength = 6 + 1; + int sppValidLength = 6 + 1; - u8 *dataPtr = (u8 *)&spp; - u16 crc = modbusCRC16((u8 *)(dataPtr + 3), 4); + u8 *dataPtr = (u8 *)&spp; + u16 crc = modbusCRC16((u8 *)(dataPtr + 3), 4); - return QByteArray((char *)&spp, sppValidLength) + - QByteArray((char *)&crc, 2); + return QByteArray((char *)&spp, sppValidLength) + + QByteArray((char *)&crc, 2); #endif #if 0 @@ -138,73 +135,73 @@ QByteArray setDeviceStartStop(const DeviceStartMode mode) return QByteArray(data,length); #endif -} - -void experimentalStateSwitching(const CommonData &cd) -{ - // Switch the software mode accord to the serial port data. - switch(cd.run_type){ - case DeviceRunStatus::Heat: - case DeviceRunStatus::ConstantTemp: - Global::_mode = Global::Mode::Experiment; - break; - case DeviceRunStatus::Idle: - case DeviceRunStatus::Cooling: - Global::_mode = Global::Mode::Analysis; - - break; - default:break; } + void experimentalStateSwitching(const CommonData &cd) + { + // Switch the software mode accord to the serial port data. + switch (cd.run_type) + { + case DeviceRunStatus::Heat: + case DeviceRunStatus::ConstantTemp: + Global::_mode = Global::Mode::Experiment; + break; + case DeviceRunStatus::Idle: + case DeviceRunStatus::Cooling: + Global::_mode = Global::Mode::Analysis; + + break; + default: + break; + } + #if 0 logde<<"phase:"<<(int)cd.current_phase; logde<<"run type:"<<(int)cd.run_type; logde<<"software mode:"<> 1) ^ 0xA001; - } - else - { - crc = crc >> 1; + if (crc & 0x0001) + { + crc = (crc >> 1) ^ 0xA001; + } + else + { + crc = crc >> 1; + } } } + // CRC结果低字节在前,高字节在后 + return crc; + } + + // 请求实验参数 + QByteArray inquirePhaseInfo() + { + SerialPortProtocol spp; + spp.head = FRANE_HEAD; + spp.cmd = READ_CMD; + spp.addr = 0x0050; + spp.len = 6; + // + spp.data_buf[0] = PHASE_BYTE_SIZE * 6 + 2; + // + u8 *dataPtr = (u8 *)&spp; + u16 crc = modbusCRC16((u8 *)(dataPtr + 3), 4); + // + int sppValidLength = 6 + 1; + return QByteArray((char *)&spp, sppValidLength) + + QByteArray((char *)&crc, 2); } - // CRC结果低字节在前,高字节在后 - return crc; -} - -QByteArray inquirePhaseInfo() -{ - SerialPortProtocol spp; - spp.head = FRANE_HEAD; - spp.cmd = READ_CMD; - spp.addr = 0x0050; - spp.len = 6; - // - spp.data_buf[0] = PHASE_BYTE_SIZE * 6 + 2; - // - u8 *dataPtr = (u8 *)&spp; - u16 crc = modbusCRC16((u8 *)(dataPtr + 3), 4); - // - int sppValidLength = 6 + 1; - return QByteArray((char *)&spp, sppValidLength) + - QByteArray((char *)&crc, 2); -} - - - }; diff --git a/src/serialport/dataparser.h b/src/serialport/dataparser.h index 1b4a031..eccd2b3 100644 --- a/src/serialport/dataparser.h +++ b/src/serialport/dataparser.h @@ -21,3 +21,8 @@ unsigned short modbusCRC16(unsigned char *data,unsigned short length); } #endif // DATAPARSER_H + +/** +中油资本 +孚日股份 + */ diff --git a/src/serialport/protocol.h b/src/serialport/protocol.h index 3f1a0db..e179beb 100644 --- a/src/serialport/protocol.h +++ b/src/serialport/protocol.h @@ -3,11 +3,11 @@ #include -#define FRANE_HEAD 0x5AA5 //枕头 +#define FRANE_HEAD 0x5AA5 //帧头 #define WRITE_CMD 0x82 //写指令 #define READ_CMD 0x83 //读指令 -#define PHASE_START_ADDR 0X0050 +#define PHASE_START_ADDR 0X0050 #define PHASE_BYTE_SIZE 12 #pragma pack(push) @@ -35,13 +35,13 @@ typedef struct com_protocol uint8_t cmd; uint16_t addr; uint8_t data_buf[256]; -}SerialPortProtocol; +} SerialPortProtocol; typedef enum gas_type { NC, N2, O2 -}GasType; +} GasType; typedef struct control_data { @@ -50,7 +50,7 @@ typedef struct control_data float temp_flow; uint16_t constant_temp_time_min; enum gas_type gas; // uint8_t -}Phase; +} Phase; struct pid_data { @@ -59,6 +59,7 @@ struct pid_data float kd; }; +#if 0 typedef struct com_data { uint8_t run_type; //0,运行状态 @@ -99,6 +100,52 @@ typedef struct com_data float enthalpy_equation_b; //热焓方程式系数b float enthalpy_equation_c; //热焓方程式系数c } CommonData; +#endif + +using rt_uint8_t = uint8_t; + +typedef struct com_data +{ + rt_uint8_t run_type; //0,运行状态 + rt_uint8_t current_phase; //1,当前阶段 + float add_run_time; //2,累计运行时间 + float add_constan_temp_time; //6,当前阶段恒温时间 + double sample_temp; //A,样品温度 + double dsc; //12,热流 + double temp_flow; //1a,升温速率 + float cold_temp; //22,冷端温度 + rt_uint8_t hardware_err; //26,硬件错误 + rt_uint8_t current_gas; //27,当前气氛 + float auto_pid_temp_flow; //28,自整定升温速率 + rt_uint8_t run_mode; //2c,启动模式 + rt_uint8_t reserve_buf[35]; //2d-4f,保留 + struct control_data phase_data[6];//50-97,阶段控制数据 + rt_uint8_t meas_type; //98,测试类型 + rt_uint8_t init_gas; //99, 初始气氛 + struct pid_data temp_flow_pid_low;//9a-a5,升温速率 低pid + struct pid_data temp_flow_pid_high;//a6-B1,升温速率 高pid + float cold_temp_cali_standard; //B2,冷端标准温度 + float cold_temp_cali_meas; //B6,冷端测量温度 + rt_uint8_t cold_cali_flag; //BA,冷端温度计算 + float cold_temp_slope; //BB,冷端温度倍率 + float sample_temp_calilow_standard;//BF,样品标准温度低倍率 + float sample_temp_calilow_meas; //C3,样品测量温度低倍率 + rt_uint8_t sample_temp_cali_low_flag;//C7,样品温度计算低倍率 + float sample_temp_slope_low; //C8,样品温度低倍率低倍率 + float sample_temp_calimid_standard;//CC,样品标准温度中倍率 + float sample_temp_calimid_meas; //D0,样品测量温度中倍率 + rt_uint8_t sample_temp_cali_mid_flag;//D4,样品温度计算中倍率 + float sample_temp_slope_middle; //D5,样品温度中倍率 + float sample_temp_calihigh_standard;//D9,样品标准温度高倍率 + float sample_temp_calihigh_meas; //DD样品测量温度高倍率 + rt_uint8_t sample_temp_cali_high_flag; //E1样品温度计算高倍率 + float sample_temp_slope_high; //E2样品温度高倍率 + rt_uint8_t cali_data_backup; //E6标定数据备份 + float enthalpy_equation_a; //热焓方程式系数a + float enthalpy_equation_b; //热焓方程式系数b + float enthalpy_equation_c; //热焓方程式系数c + +}CommonData; #pragma pack(pop) diff --git a/src/serialport/serialport.cpp b/src/serialport/serialport.cpp index d934b69..aa8c631 100644 --- a/src/serialport/serialport.cpp +++ b/src/serialport/serialport.cpp @@ -15,16 +15,17 @@ using namespace std; -const u16 conVid = 1155; // 0x0483 -const u16 conPid = 22336; // 0x5740 +const u16 conVid = 1155; // 0x0483 +const u16 conPid = 22336; // 0x5740 SerialPort::SerialPort(QObject *parent) : QObject(parent), _sp(new QSerialPort(this)), - _portCheckTimer(new QTimer(this)) { + _portCheckTimer(new QTimer(this)) +{ // connect(_portCheckTimer, &QTimer::timeout, this, &SerialPort::slotPortCheck); - _portCheckTimer->start(1000); // 每秒检测一次 + _portCheckTimer->start(1000); // 每秒检测一次 // displayPortInfo(); @@ -77,12 +78,14 @@ SerialPort::SerialPort(QObject *parent) #endif } -SerialPort *SerialPort::instance() { +SerialPort *SerialPort::instance() +{ static SerialPort ins; return &ins; } -SerialPort::~SerialPort() { +SerialPort::~SerialPort() +{ logde << "serialport destructor."; closeSp(); @@ -95,9 +98,11 @@ SerialPort::~SerialPort() { _portCheckTimer = nullptr; } -void SerialPort::slotReadData() { +void SerialPort::slotReadData() +{ QByteArray ba = _sp->readAll(); - if (ba.size() == 0) { + if (ba.size() == 0) + { return; } @@ -107,42 +112,74 @@ void SerialPort::slotReadData() { #endif SerialPortProtocol *spp = (SerialPortProtocol *)ba.data(); - if (FRANE_HEAD != spp->head) { + if (FRANE_HEAD != spp->head) + { logde << "Data header error."; return; } - // phase setting data - if (spp->addr == PHASE_START_ADDR) { + // 实验阶段数据设置 + if (spp->addr == PHASE_START_ADDR) + { emit sigSendPhaseInfo(ba); return; } int dataLength = spp->len - 5; + // 非实验阶段数据解析 CommonData cd; - u8 *cdPtr = (u8 *)&cd; + u8 *cdPtr = (u8 *)&cd; memcpy(cdPtr + spp->addr, spp->data_buf, dataLength); - if (WRITE_CMD == spp->cmd) { + // 写指令 + if (WRITE_CMD == spp->cmd) + { + logde << "write cmd..."; + + QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 + logde << "receive info (hex):" << hexData.toStdString(); + + logde << "addr:" << spp->addr; + logde << "init gas type:" << (int)cd.init_gas; + commonDataParser(dataLength, spp->addr, cd); - } else if (READ_CMD == spp->cmd) { + + logde << "meas type:" << (int)cd.meas_type; + logde << "init gas:" << (int)cd.init_gas; + + if(spp->addr == 0x99){ + logde<<"init gas type:"<<(int)spp->data_buf[0]; + Global::_experimentInfo.initialAtmosPhere = (GasType)spp->data_buf[0]; + } + } + else if (READ_CMD == spp->cmd) + { + // 读指令 + // logde<<"read cmd..."; + // DataParser::experimentalStateSwitching(cd); + updateStatus(cd); - if (spp->addr == 0) { - if (Global::Mode::Experiment == Global::_mode) { + if (spp->addr == 0) + { + if (Global::Mode::Experiment == Global::_mode) + { emit sigSendCommonData(cd); } // emit sigSendCommonDataToRealDataForm(cd); - } else { - QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 + } + else + { + QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 logde << "receive info (hex):" << hexData.toStdString(); } } } -void SerialPort::updateStatus(const CommonData &cd) { +void SerialPort::updateStatus(const CommonData &cd) +{ #if 0 // logde<<"phase:"<<(int)cd.current_phase; // logde<<"run type:"<<(int)cd.run_type; @@ -150,32 +187,37 @@ void SerialPort::updateStatus(const CommonData &cd) { #endif // Switch the software mode accord to the serial port data. - switch (cd.run_type) { - case DeviceRunStatus::Heat: - case DeviceRunStatus::ConstantTemp: - if (Global::_mode != Global::Mode::Experiment) { - Global::_mode = Global::Mode::Experiment; - } - break; - case DeviceRunStatus::Idle: - case DeviceRunStatus::Cooling: - if (Global::_mode != Global::Mode::Analysis) { - Global::_mode = Global::Mode::Analysis; - } - break; - default: - break; + switch (cd.run_type) + { + case DeviceRunStatus::Heat: + case DeviceRunStatus::ConstantTemp: + if (Global::_mode != Global::Mode::Experiment) + { + Global::_mode = Global::Mode::Experiment; + } + break; + case DeviceRunStatus::Idle: + case DeviceRunStatus::Cooling: + if (Global::_mode != Global::Mode::Analysis) + { + Global::_mode = Global::Mode::Analysis; + } + break; + default: + break; } // 判断是否根据下位机数据,切换软件实验模式。 - static Global::Mode preMode = Global::Mode::Analysis; - bool sendSaveSignalFlag = false; - if (Global::_mode != preMode) { - if (preMode == Global::Mode::Experiment - && Global::_mode == Global::Mode::Analysis) { + static Global::Mode preMode = Global::Mode::Analysis; + bool sendSaveSignalFlag = false; + if (Global::_mode != preMode) + { + if (preMode == Global::Mode::Experiment && Global::_mode == Global::Mode::Analysis) + { sendSaveSignalFlag = true; - } else if (preMode == Global::Mode::Analysis - && Global::_mode == Global::Mode::Experiment) { + } + else if (preMode == Global::Mode::Analysis && Global::_mode == Global::Mode::Experiment) + { // 根据下位机数据,软件进入实验模式,开始实验。 logde << "start experiment, accord to device data..."; @@ -188,8 +230,10 @@ void SerialPort::updateStatus(const CommonData &cd) { } // If phase update, add new phase data to the global param. - if (Global::_mode == Global::Mode::Experiment) { - if (Global::_currentPhase != (int)cd.current_phase) { + if (Global::_mode == Global::Mode::Experiment) + { + if (Global::_currentPhase != (int)cd.current_phase) + { logde << "mode experiment,phase not equeal."; @@ -215,31 +259,35 @@ void SerialPort::updateStatus(const CommonData &cd) { updateAxis(); // Update axis. - bool experimentEnded = false; + bool experimentEnded = false; QString devRunModeStr; - switch (cd.run_type) { - case DeviceRunStatus::Heat: - devRunModeStr = "升温"; - experimentEnded = true; - break; - case DeviceRunStatus::ConstantTemp: - devRunModeStr = "实验"; - experimentEnded = true; - break; - case DeviceRunStatus::Idle: - case DeviceRunStatus::Cooling: - devRunModeStr = "冷却"; - experimentEnded = false; - break; - break; - default: - break; + switch (cd.run_type) + { + case DeviceRunStatus::Heat: + devRunModeStr = "升温"; + experimentEnded = true; + break; + case DeviceRunStatus::ConstantTemp: + devRunModeStr = "实验"; + experimentEnded = true; + break; + case DeviceRunStatus::Idle: + case DeviceRunStatus::Cooling: + devRunModeStr = "冷却"; + experimentEnded = false; + break; + break; + default: + break; } QString msg; - if (experimentEnded) { + if (experimentEnded) + { msg = QString("设备运行状态:%2 实验阶段:%3 实验进行中").arg(devRunModeStr).arg(cd.current_phase); - } else { + } + else + { // msg = QString("设备运行状态:%2 实验阶段:%3 实验结束").arg(devRunModeStr).arg(cd.current_phase); msg = QString("设备运行状态:%2 实验结束").arg(devRunModeStr); } @@ -269,22 +317,30 @@ void SerialPort::updateStatus(const CommonData &cd) { #endif // 弹出保存数据对话框。 - if (sendSaveSignalFlag && !Global::_OITAutoAnalysisModeFlag) { + if (sendSaveSignalFlag && !Global::_OITAutoAnalysisModeFlag) + { emit sigSaveExperimentalDataMsgBox(); } } +// 解析下位机数据,更新全局参数。 void SerialPort::commonDataParser(const int dataLength, const u16 addr, - const CommonData &cd) { - int localLength = dataLength; - int localAddr = addr; + const CommonData &cd) +{ - int phaseByteSize = sizeof(Phase); - auto phaseParserFunc = [&](const int index) { + int localLength = dataLength; + int localAddr = addr; + + logde << "localLength total:" << localLength; + logde << "localAddr total:" << localAddr; + + int phaseByteSize = sizeof(Phase); + auto phaseParserFunc = [&](const int index) + { Phase phase; - phase.onoff = 1; - phase.gas = cd.phase_data[index].gas; - phase.temp_flow = cd.phase_data[index].temp_flow; + phase.onoff = 1; + phase.gas = cd.phase_data[index].gas; + phase.temp_flow = cd.phase_data[index].temp_flow; phase.cutoff_temp = cd.phase_data[index].cutoff_temp; phase.constant_temp_time_min = cd.phase_data[index].constant_temp_time_min; @@ -293,81 +349,103 @@ void SerialPort::commonDataParser(const int dataLength, const u16 addr, localAddr += phaseByteSize; }; - while (localLength) { + logde << "init_gas offset:" << offsetof(CommonData, init_gas); + + while (localLength) + { logde << "localLength:" << localLength; - switch (localAddr) { - case offsetof(CommonData, run_type): // 运行状态 + switch (localAddr) + { + case offsetof(CommonData, run_type): // 运行状态 + { + switch (cd.run_type) { - switch (cd.run_type) { - case DeviceRunStatus::Cooling: - // Global::instance()->setMode(Global::Mode::Analysis); - Global::_mode = Global::Mode::Analysis; - logde << "set global mode analysis.common data parser."; - break; - default: - break; - } - - localAddr += 1; - localLength -= 1; - - break; - } - case offsetof(CommonData, current_gas): // 当前气氛 - // gas_type_set(dev->temp, msg_data.current_gas); - localAddr += 1; - localLength -= 1; - break; - case offsetof(CommonData, auto_pid_temp_flow): // 自整定升温速率 - localAddr += 4; - localLength -= 4; - break; - case offsetof(CommonData, run_mode): { - DeviceStartMode mode = (DeviceStartMode)cd.run_mode; - - switch (mode) { - case DeviceStartMode::Stop: - Global::_mode = Global::Mode::Analysis; - logde << "set global mode analysis."; - break; - case DeviceStartMode::Start: - Global::_mode = Global::Mode::Experiment; - break; - default: - break; - } - // - localLength--; - localAddr++; - - break; - } - case offsetof(CommonData, phase_data[0].onoff): - phaseParserFunc(0); - break; - case offsetof(CommonData, phase_data[1].onoff): - phaseParserFunc(1); - break; - case offsetof(CommonData, phase_data[2].onoff): - phaseParserFunc(2); - break; - case offsetof(CommonData, phase_data[3].onoff): - phaseParserFunc(3); - break; - case offsetof(CommonData, phase_data[4].onoff): - phaseParserFunc(4); - break; - case offsetof(CommonData, phase_data[5].onoff): - phaseParserFunc(5); + case DeviceRunStatus::Cooling: + // Global::instance()->setMode(Global::Mode::Analysis); + Global::_mode = Global::Mode::Analysis; + logde << "set global mode analysis.common data parser."; break; default: - localLength--; break; + } + + localAddr += 1; + localLength -= 1; + + break; + } + case offsetof(CommonData, current_gas): // 当前气氛 + // gas_type_set(dev->temp, msg_data.current_gas); + localAddr += 1; + localLength -= 1; + break; + case offsetof(CommonData, auto_pid_temp_flow): // 自整定升温速率 + localAddr += 4; + localLength -= 4; + break; + case offsetof(CommonData, run_mode): + { + DeviceStartMode mode = (DeviceStartMode)cd.run_mode; + + switch (mode) + { + case DeviceStartMode::Stop: + Global::_mode = Global::Mode::Analysis; + logde << "set global mode analysis."; + break; + case DeviceStartMode::Start: + Global::_mode = Global::Mode::Experiment; + break; + default: + break; + } + // + localLength--; + localAddr++; + + break; + } + case offsetof(CommonData, phase_data[0].onoff): + phaseParserFunc(0); + break; + case offsetof(CommonData, phase_data[1].onoff): + phaseParserFunc(1); + break; + case offsetof(CommonData, phase_data[2].onoff): + phaseParserFunc(2); + break; + case offsetof(CommonData, phase_data[3].onoff): + phaseParserFunc(3); + break; + case offsetof(CommonData, phase_data[4].onoff): + phaseParserFunc(4); + break; + case offsetof(CommonData, phase_data[5].onoff): + phaseParserFunc(5); + break; + // 测试类型 + case offsetof(CommonData, meas_type): + logde << "meas_type:" << cd.meas_type; + + localLength--; + localAddr++; + break; + // 初始气氛 + case offsetof(CommonData, init_gas): + logde << "init_gas:" << cd.init_gas; + + localLength--; + localAddr++; + break; + default: + localLength--; + break; }; } } -bool SerialPort::openSp() { +bool SerialPort::openSp() +{ logde << "openSp 1"; closeSp(); QThread::msleep(100); @@ -379,17 +457,20 @@ bool SerialPort::openSp() { #endif logde << "openSp 2"; - foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { + foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) + { u16 pid = info.productIdentifier(); u16 vid = info.vendorIdentifier(); - if ((pid == conPid) && (vid == conVid)) { + if ((pid == conPid) && (vid == conVid)) + { _sp->setPort(info); break; } } logde << "openSp 3"; - if (_sp == nullptr) { + if (_sp == nullptr) + { qDebug() << "Device not found."; return false; } @@ -397,10 +478,10 @@ bool SerialPort::openSp() { // 设置波特率和读写方向 _sp->setBaudRate(QSerialPort::Baud115200, QSerialPort::AllDirections); - _sp->setDataBits(QSerialPort::Data8); // 数据位为8位 - _sp->setFlowControl(QSerialPort::NoFlowControl); // 无流控制 - _sp->setParity(QSerialPort::NoParity); // 无校验位 - _sp->setStopBits(QSerialPort::OneStop); // 一位停止位 + _sp->setDataBits(QSerialPort::Data8); // 数据位为8位 + _sp->setFlowControl(QSerialPort::NoFlowControl); // 无流控制 + _sp->setParity(QSerialPort::NoParity); // 无校验位 + _sp->setStopBits(QSerialPort::OneStop); // 一位停止位 // 4.连接信号槽 connect(_sp, &QSerialPort::readyRead, @@ -408,10 +489,13 @@ bool SerialPort::openSp() { logde << "openSp 4"; // 2.打开串口 - if (!_sp->open(QIODevice::ReadWrite)) { + if (!_sp->open(QIODevice::ReadWrite)) + { qDebug() << "open failed." << _sp->error(); return false; - } else { + } + else + { // qDebug() << "open succ."; logde << "openSp 5."; // 设置 DTR 信号为就绪状态(true 表示低电平) @@ -422,29 +506,37 @@ bool SerialPort::openSp() { return true; } -bool SerialPort::closeSp() { - if (_sp && _sp->isOpen()) { +bool SerialPort::closeSp() +{ + if (_sp && _sp->isOpen()) + { _sp->clear(); _sp->close(); } } -void SerialPort::sendCmd(const SerialPort::E_CMD_TYPE e) { +void SerialPort::sendCmd(const SerialPort::E_CMD_TYPE e) +{ #if 1 - int length = 20; - char in_char[21] = { '\0' }; + int length = 20; + char in_char[21] = {'\0'}; - if (e == e_zero) { + if (e == e_zero) + { // char data[10] = {'55','aa','0b','0a','20', // '4e','00','00','00','c3'}; const char *data = "55aa0b0a204e000000c3"; memcpy(in_char, data, length); - } else if (e == e_back) { + } + else if (e == e_back) + { // char data[10] = {'55','aa','08','10','27', // 'f0','d8','ff','ff','c3'}; const char *data = "55aa081027f0d8ffffc3"; memcpy(in_char, data, length); - } else if (e == e_forward) { + } + else if (e == e_forward) + { // char data[10] = {'55','aa','08','10','27', // '10','27','00','00','c3'}; const char *data = "55aa08102710270000c3"; @@ -454,21 +546,26 @@ void SerialPort::sendCmd(const SerialPort::E_CMD_TYPE e) { #endif // char in_char[] = "55aa0b0a204e000000c3"; - char out_char[21] = { '\0' }; - int hex_length = 10; + char out_char[21] = {'\0'}; + int hex_length = 10; toHex(in_char, hex_length, out_char); int num = _sp->write(out_char, hex_length); - if (num == -1) { + if (num == -1) + { qDebug() << "write failed."; - } else { + } + else + { qDebug() << "write ok|num:" << num; } } -void SerialPort::toHex(char *in_char, int char_length, char *out_char) { - while (char_length--) { +void SerialPort::toHex(char *in_char, int char_length, char *out_char) +{ + while (char_length--) + { *out_char = (*in_char & 0x40 ? *in_char + 9 : *in_char) << 4; ++in_char; *out_char |= (*in_char & 0x40 ? *in_char + 9 : *in_char) & 0xF; @@ -477,12 +574,14 @@ void SerialPort::toHex(char *in_char, int char_length, char *out_char) { } } -void SerialPort::displayPortInfo() { +void SerialPort::displayPortInfo() +{ // 获取系统中所有可用的串口信息 QList serialPorts = QSerialPortInfo::availablePorts(); // 遍历每个串口信息 - for (const QSerialPortInfo &portInfo : serialPorts) { + for (const QSerialPortInfo &portInfo : serialPorts) + { qDebug() << "================================================"; // 打印串口的名称 qDebug() << "串口名称: " << portInfo.portName(); @@ -499,14 +598,16 @@ void SerialPort::displayPortInfo() { // 打印是否有产品标识符 qDebug() << "是否有产品标识符: " << (portInfo.hasProductIdentifier() ? "是" : "否"); // 如果有虚拟调制解调器,打印其标识符 - if (portInfo.hasVendorIdentifier()) { + if (portInfo.hasVendorIdentifier()) + { qint16 vid = portInfo.vendorIdentifier(); qDebug() << "虚拟调制解调器标识符: " << vid; QString hexStr = QString("0x%1").arg(vid, 4, 16, QChar('0')).toUpper(); qDebug() << "vid 0x" << hexStr; } // 如果有产品标识符,打印其标识符 - if (portInfo.hasProductIdentifier()) { + if (portInfo.hasProductIdentifier()) + { qint16 pid = portInfo.productIdentifier(); qDebug() << "产品标识符: " << pid; QString hexStr = QString("0x%1").arg(pid, 4, 16, QChar('0')).toUpper(); @@ -515,29 +616,34 @@ void SerialPort::displayPortInfo() { } } -void SerialPort::updateAxis() { - static Global::Mode previousMode = Global::Analysis; // 记录上一次的模式 +void SerialPort::updateAxis() +{ + static Global::Mode previousMode = Global::Analysis; // 记录上一次的模式 - if (previousMode == Global::Analysis && Global::_mode == Global::Experiment) { + if (previousMode == Global::Analysis && Global::_mode == Global::Experiment) + { // std::cout << "Mode has changed from Analysis to Experiment!" << std::endl; Global::ExperimentInfo &eti = Global::_experimentInfo; - if (eti.phaseVtr.size() > 0) { + if (eti.phaseVtr.size() > 0) + { logde << "serialport set axis."; float temp = eti.phaseVtr.at(0).cutoff_temp; - emit sigAxisModify(temp); + emit sigAxisModify(temp); } } - if (previousMode != Global::_mode) { + if (previousMode != Global::_mode) + { previousMode = Global::_mode; } } -void SerialPort::parserTest() { - const uchar data[] = { 0xa5, 0x5a, 0x2d, 0x83, - 0x00, 0x00, - 0x00, 0x01, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x23, - 0x23, 0x07, 0xbc, 0xb5, 0xf2, 0xc8, 0x57, 0x38, 0x40, 0x1b, 0x63, 0x27, 0xbc, 0x04, 0xa7, 0xf2, 0x3f, 0x55, 0x55, 0x55, 0xd5, 0x04, 0xf3, 0xab, 0xbf, 0xfa, 0x2b, 0xcd, 0x41, 0x00, 0x00, 0x93, 0xba }; +void SerialPort::parserTest() +{ + const uchar data[] = {0xa5, 0x5a, 0x2d, 0x83, + 0x00, 0x00, + 0x00, 0x01, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x23, + 0x23, 0x07, 0xbc, 0xb5, 0xf2, 0xc8, 0x57, 0x38, 0x40, 0x1b, 0x63, 0x27, 0xbc, 0x04, 0xa7, 0xf2, 0x3f, 0x55, 0x55, 0x55, 0xd5, 0x04, 0xf3, 0xab, 0xbf, 0xfa, 0x2b, 0xcd, 0x41, 0x00, 0x00, 0x93, 0xba}; CommonData *serialPortData = nullptr; qDebug() << "data length:" << sizeof(data) / sizeof(uchar); @@ -572,49 +678,64 @@ void SerialPort::parserTest() { #endif } -void SerialPort::slotDeliverData(const QByteArray &ba) { +void SerialPort::slotDeliverData(const QByteArray &ba) +{ openSp(); slotSendData(ba); } -void SerialPort::slotSendData(const QByteArray &ba) { +void SerialPort::slotSendData(const QByteArray &ba) +{ #if 1 logde << "slotSendData =========================="; logde << "slotSendData:" << ba.size(); - QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 + QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 logde << "slotSendData:" << hexData.toStdString(); #endif - if (_sp != nullptr && _sp->isOpen()) { + if (_sp != nullptr && _sp->isOpen()) + { int bytesWritten = _sp->write(ba); - if (bytesWritten == -1) { + if (bytesWritten == -1) + { logde << "Write failed." << _sp->errorString().toStdString(); - } else if (bytesWritten != ba.size()) { + } + else if (bytesWritten != ba.size()) + { logde << "Not all data was writen."; - } else { + } + else + { logde << "All data writen."; } - } else { + } + else + { logde << "sp not open."; return; } } -void SerialPort::slotCloseSp() { - if (_sp != nullptr && _sp->isOpen()) { +void SerialPort::slotCloseSp() +{ + if (_sp != nullptr && _sp->isOpen()) + { _sp->close(); } } -void SerialPort::slotPortCheck() { - // logde<<"slotPortCheck..."; +void SerialPort::slotPortCheck() +{ + // logde<<"slotPortCheck..."; - foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) { + foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts()) + { u16 pid = info.productIdentifier(); u16 vid = info.vendorIdentifier(); - if ((pid == conPid) && (vid == conVid)) { - // logde<<"slotPortCheck... device connected."; + if ((pid == conPid) && (vid == conVid)) + { + // logde<<"slotPortCheck... device connected."; return; } } @@ -622,5 +743,5 @@ void SerialPort::slotPortCheck() { // logde<<"device disconnected."; emit sigDeviceDisconnected(); - // logde<<"slotPortCheck... device disconnected."; + // logde<<"slotPortCheck... device disconnected."; } diff --git a/src/ui/experimentsettingform.cpp b/src/ui/experimentsettingform.cpp index 024cee1..ca5ba9f 100644 --- a/src/ui/experimentsettingform.cpp +++ b/src/ui/experimentsettingform.cpp @@ -10,15 +10,17 @@ #include "filemanager.h" #include "logger.h" -QString extractObjectName(const QString &fullObjectName) { - return fullObjectName.mid(4); // 去掉前 4 个字符("ui->") +QString extractObjectName(const QString &fullObjectName) +{ + return fullObjectName.mid(4); // 去掉前 4 个字符("ui->") } #define SET_OBJECT_NAME(WIDGET) \ WIDGET->setObjectName(extractObjectName(#WIDGET)) ExperimentSettingForm::ExperimentSettingForm(QWidget *parent) : QWidget(parent), - ui(new Ui::ExperimentSettingForm) { + ui(new Ui::ExperimentSettingForm) +{ ui->setupUi(this); ui->sampleNameLineEdit->setText("new"); @@ -52,11 +54,13 @@ ExperimentSettingForm::ExperimentSettingForm(QWidget *parent) : QWidget(parent), this, &ExperimentSettingForm::slotCancel); } -ExperimentSettingForm::~ExperimentSettingForm() { +ExperimentSettingForm::~ExperimentSettingForm() +{ delete ui; } -void ExperimentSettingForm::uiReset() { +void ExperimentSettingForm::uiReset() +{ // init data and status. // default value @@ -164,84 +168,90 @@ void ExperimentSettingForm::uiReset() { SET_OBJECT_NAME(ui->comboBox_initial_atmosphere); } -void ExperimentSettingForm::uiSetPhaseEnable(const int index) { - switch (index) { - case 1: - ui->checkBox_phase_1->setTristate(true); - ui->phase_1_cutoff_temp->setEnabled(true); - ui->phase_1_scan_rate->setEnabled(true); - ui->phase_1_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - case 2: - ui->checkBox_phase_2->setTristate(true); - ui->phase_2_cutoff_temp->setEnabled(true); - ui->phase_2_scan_rate->setEnabled(true); - ui->phase_3_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - case 3: - ui->checkBox_phase_1->setTristate(true); - ui->phase_1_cutoff_temp->setEnabled(true); - ui->phase_1_scan_rate->setEnabled(true); - ui->phase_1_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - case 4: - ui->checkBox_phase_1->setTristate(true); - ui->phase_1_cutoff_temp->setEnabled(true); - ui->phase_1_scan_rate->setEnabled(true); - ui->phase_1_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - case 5: - ui->checkBox_phase_1->setTristate(true); - ui->phase_1_cutoff_temp->setEnabled(true); - ui->phase_1_scan_rate->setEnabled(true); - ui->phase_1_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - case 6: - ui->checkBox_phase_1->setTristate(true); - ui->phase_1_cutoff_temp->setEnabled(true); - ui->phase_1_scan_rate->setEnabled(true); - ui->phase_1_constant_temp->setEnabled(true); - ui->comboBox_phase_1_atmosphere->setEnabled(true); - break; - default: - break; +void ExperimentSettingForm::uiSetPhaseEnable(const int index) +{ + switch (index) + { + case 1: + ui->checkBox_phase_1->setTristate(true); + ui->phase_1_cutoff_temp->setEnabled(true); + ui->phase_1_scan_rate->setEnabled(true); + ui->phase_1_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + case 2: + ui->checkBox_phase_2->setTristate(true); + ui->phase_2_cutoff_temp->setEnabled(true); + ui->phase_2_scan_rate->setEnabled(true); + ui->phase_3_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + case 3: + ui->checkBox_phase_1->setTristate(true); + ui->phase_1_cutoff_temp->setEnabled(true); + ui->phase_1_scan_rate->setEnabled(true); + ui->phase_1_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + case 4: + ui->checkBox_phase_1->setTristate(true); + ui->phase_1_cutoff_temp->setEnabled(true); + ui->phase_1_scan_rate->setEnabled(true); + ui->phase_1_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + case 5: + ui->checkBox_phase_1->setTristate(true); + ui->phase_1_cutoff_temp->setEnabled(true); + ui->phase_1_scan_rate->setEnabled(true); + ui->phase_1_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + case 6: + ui->checkBox_phase_1->setTristate(true); + ui->phase_1_cutoff_temp->setEnabled(true); + ui->phase_1_scan_rate->setEnabled(true); + ui->phase_1_constant_temp->setEnabled(true); + ui->comboBox_phase_1_atmosphere->setEnabled(true); + break; + default: + break; } } -GasType ExperimentSettingForm::conver2GasType(const int index) { - switch (index) { - case 0: - return GasType::NC; - break; // 假设 GAS_AIR 是 gas_type 的枚举值 - case 1: - return GasType::N2; - break; - case 2: - return GasType::O2; - break; - default: - return GasType::NC; - break; // 处理未知情况 +GasType ExperimentSettingForm::conver2GasType(const int index) +{ + switch (index) + { + case 0: + return GasType::NC; + break; // 假设 GAS_AIR 是 gas_type 的枚举值 + case 1: + return GasType::N2; + break; + case 2: + return GasType::O2; + break; + default: + return GasType::NC; + break; // 处理未知情况 } return GasType::NC; } -bool ExperimentSettingForm::phaseCufoffTempAndTempFlowCheck(const int phaseIndex, +bool ExperimentSettingForm::phaseCufoffTempAndTempFlowCheck(const int phaseIndex, const float prePhaseCufoffTemp, const float currentPhaseCufoffTemp, - const float tempFlow) { - if (tempFlow < 0) { + const float tempFlow) +{ + if (tempFlow < 0) + { phaseScanRateErrorMesgBox(phaseIndex); return false; } - if (prePhaseCufoffTemp != currentPhaseCufoffTemp - && tempFlow == 0) { + if (prePhaseCufoffTemp != currentPhaseCufoffTemp && tempFlow == 0) + { phaseScanRateErrorMesgBox(phaseIndex); return false; } @@ -249,19 +259,26 @@ bool ExperimentSettingForm::phaseCufoffTempAndTempFlowCheck(const int phaseInd return true; } -void ExperimentSettingForm::slotPhase2StateChanged(int state) { +void ExperimentSettingForm::slotPhase2StateChanged(int state) +{ qDebug() << "slotPhase2StateChanged:" << state; - if (state == Qt::PartiallyChecked) { + if (state == Qt::PartiallyChecked) + { ui->phase_2_cutoff_temp->setEnabled(true); ui->phase_2_scan_rate->setEnabled(true); ui->phase_2_constant_temp->setEnabled(true); ui->comboBox_phase_2_atmosphere->setEnabled(true); - } else { + } + else + { // unchecked - if (ui->checkBox_phase_3->isChecked()) { + if (ui->checkBox_phase_3->isChecked()) + { ui->checkBox_phase_2->setChecked(true); - } else { + } + else + { ui->phase_2_cutoff_temp->setEnabled(false); ui->phase_2_scan_rate->setEnabled(false); ui->phase_2_constant_temp->setEnabled(false); @@ -270,20 +287,30 @@ void ExperimentSettingForm::slotPhase2StateChanged(int state) { } } -void ExperimentSettingForm::slotPhase3StateChanged(int state) { - if (state == Qt::PartiallyChecked) { - if (ui->checkBox_phase_2->isChecked()) { +void ExperimentSettingForm::slotPhase3StateChanged(int state) +{ + if (state == Qt::PartiallyChecked) + { + if (ui->checkBox_phase_2->isChecked()) + { ui->phase_3_cutoff_temp->setEnabled(true); ui->phase_3_scan_rate->setEnabled(true); ui->phase_3_constant_temp->setEnabled(true); ui->comboBox_phase_3_atmosphere->setEnabled(true); - } else { + } + else + { ui->checkBox_phase_3->setChecked(false); } - } else { - if (ui->checkBox_phase_4->isChecked()) { + } + else + { + if (ui->checkBox_phase_4->isChecked()) + { ui->checkBox_phase_3->setChecked(true); - } else { + } + else + { ui->phase_3_cutoff_temp->setEnabled(false); ui->phase_3_scan_rate->setEnabled(false); ui->phase_3_constant_temp->setEnabled(false); @@ -292,20 +319,30 @@ void ExperimentSettingForm::slotPhase3StateChanged(int state) { } } -void ExperimentSettingForm::slotPhase4StateChanged(int state) { - if (state == Qt::PartiallyChecked) { - if (ui->checkBox_phase_3->isChecked()) { +void ExperimentSettingForm::slotPhase4StateChanged(int state) +{ + if (state == Qt::PartiallyChecked) + { + if (ui->checkBox_phase_3->isChecked()) + { ui->phase_4_cutoff_temp->setEnabled(true); ui->phase_4_scan_rate->setEnabled(true); ui->phase_4_constant_temp->setEnabled(true); ui->comboBox_phase_4_atmosphere->setEnabled(true); - } else { + } + else + { ui->checkBox_phase_4->setChecked(false); } - } else { - if (ui->checkBox_phase_5->isChecked()) { + } + else + { + if (ui->checkBox_phase_5->isChecked()) + { ui->checkBox_phase_4->setChecked(true); - } else { + } + else + { ui->phase_4_cutoff_temp->setEnabled(false); ui->phase_4_scan_rate->setEnabled(false); ui->phase_4_constant_temp->setEnabled(false); @@ -314,20 +351,30 @@ void ExperimentSettingForm::slotPhase4StateChanged(int state) { } } -void ExperimentSettingForm::slotPhase5StateChanged(int state) { - if (state == Qt::PartiallyChecked) { - if (ui->checkBox_phase_4->isChecked()) { +void ExperimentSettingForm::slotPhase5StateChanged(int state) +{ + if (state == Qt::PartiallyChecked) + { + if (ui->checkBox_phase_4->isChecked()) + { ui->phase_5_cutoff_temp->setEnabled(true); ui->phase_5_scan_rate->setEnabled(true); ui->phase_5_constant_temp->setEnabled(true); ui->comboBox_phase_5_atmosphere->setEnabled(true); - } else { + } + else + { ui->checkBox_phase_5->setChecked(false); } - } else { - if (ui->checkBox_phase_6->isChecked()) { + } + else + { + if (ui->checkBox_phase_6->isChecked()) + { ui->checkBox_phase_5->setChecked(true); - } else { + } + else + { ui->phase_5_cutoff_temp->setEnabled(false); ui->phase_5_scan_rate->setEnabled(false); ui->phase_5_constant_temp->setEnabled(false); @@ -336,27 +383,35 @@ void ExperimentSettingForm::slotPhase5StateChanged(int state) { } } -void ExperimentSettingForm::slotPhase6StateChanged(int state) { +void ExperimentSettingForm::slotPhase6StateChanged(int state) +{ qDebug() << "slotPhase2StateChanged:" << state; - if (state == Qt::PartiallyChecked) { - if (ui->checkBox_phase_5->isChecked()) { + if (state == Qt::PartiallyChecked) + { + if (ui->checkBox_phase_5->isChecked()) + { ui->phase_6_cutoff_temp->setEnabled(true); ui->phase_6_scan_rate->setEnabled(true); ui->phase_6_constant_temp->setEnabled(true); ui->comboBox_phase_6_atmosphere->setEnabled(true); - } else { + } + else + { ui->checkBox_phase_6->setChecked(false); } - } else { + } + else + { ui->phase_6_cutoff_temp->setEnabled(false); ui->phase_6_scan_rate->setEnabled(false); ui->phase_6_constant_temp->setEnabled(false); ui->comboBox_phase_6_atmosphere->setEnabled(false); } } - -void ExperimentSettingForm::on_pushButton_deliverData_clicked() { +// 发布数据 +void ExperimentSettingForm::on_pushButton_deliverData_clicked() +{ float phase1CutoffTemp = 0.0; float phase2CutoffTemp = 0.0; float phase3CutoffTemp = 0.0; @@ -367,9 +422,9 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { QVector phaseVtr; { Phase phase; - phase.onoff = ui->checkBox_phase_1->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_1_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_1_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_1->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_1_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_1_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_1_constant_temp->text().toInt(); phase.gas = conver2GasType(ui->comboBox_phase_1_atmosphere->currentIndex()); @@ -378,7 +433,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { // phase1CutoffTemp = phase.cutoff_temp; // - if (phase.temp_flow <= 0) { + if (phase.temp_flow <= 0) + { phaseScanRateErrorMesgBox(1); return; } @@ -386,18 +442,20 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { { Phase phase; - phase.onoff = ui->checkBox_phase_2->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_2_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_2_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_2->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_2_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_2_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_2_constant_temp->text().toInt(); - phase.gas = conver2GasType(ui->comboBox_phase_2_atmosphere->currentIndex()); + phase.gas = conver2GasType(ui->comboBox_phase_2_atmosphere->currentIndex()); phaseVtr.push_back(phase); // - if (phase.onoff) { + if (phase.onoff) + { logde << "phase 2 enable."; phase2CutoffTemp = phase.cutoff_temp; - if (phase2CutoffTemp < phase1CutoffTemp) { + if (phase2CutoffTemp < phase1CutoffTemp) + { phaseCufoffTempErrorMesgBox(2); return; } @@ -418,7 +476,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { if (!phaseCufoffTempAndTempFlowCheck(2, phase1CutoffTemp, phase2CutoffTemp, - phase.temp_flow)) { + phase.temp_flow)) + { return; } } @@ -426,18 +485,20 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { { Phase phase; - phase.onoff = ui->checkBox_phase_3->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_3_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_3_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_3->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_3_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_3_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_3_constant_temp->text().toInt(); - phase.gas = conver2GasType(ui->comboBox_phase_3_atmosphere->currentIndex()); + phase.gas = conver2GasType(ui->comboBox_phase_3_atmosphere->currentIndex()); phaseVtr.push_back(phase); // - if (phase.onoff) { + if (phase.onoff) + { logde << "phase 3 enable."; phase3CutoffTemp = phase.cutoff_temp; - if (phase3CutoffTemp < phase2CutoffTemp) { + if (phase3CutoffTemp < phase2CutoffTemp) + { phaseCufoffTempErrorMesgBox(3); return; } @@ -450,7 +511,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { if (!phaseCufoffTempAndTempFlowCheck(3, phase2CutoffTemp, phase3CutoffTemp, - phase.temp_flow)) { + phase.temp_flow)) + { return; } } @@ -458,18 +520,20 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { { Phase phase; - phase.onoff = ui->checkBox_phase_4->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_4_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_4_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_4->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_4_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_4_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_4_constant_temp->text().toInt(); - phase.gas = conver2GasType(ui->comboBox_phase_4_atmosphere->currentIndex()); + phase.gas = conver2GasType(ui->comboBox_phase_4_atmosphere->currentIndex()); phaseVtr.push_back(phase); // - if (phase.onoff) { + if (phase.onoff) + { logde << "phase 4 enable."; phase4CutoffTemp = phase.cutoff_temp; - if (phase4CutoffTemp < phase3CutoffTemp) { + if (phase4CutoffTemp < phase3CutoffTemp) + { phaseCufoffTempErrorMesgBox(4); return; } @@ -482,7 +546,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { if (!phaseCufoffTempAndTempFlowCheck(4, phase3CutoffTemp, phase4CutoffTemp, - phase.temp_flow)) { + phase.temp_flow)) + { return; } } @@ -490,18 +555,20 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { { Phase phase; - phase.onoff = ui->checkBox_phase_5->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_5_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_5_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_5->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_5_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_5_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_5_constant_temp->text().toInt(); - phase.gas = conver2GasType(ui->comboBox_phase_5_atmosphere->currentIndex()); + phase.gas = conver2GasType(ui->comboBox_phase_5_atmosphere->currentIndex()); phaseVtr.push_back(phase); // - if (phase.onoff) { + if (phase.onoff) + { logde << "phase 5 enable."; phase5CutoffTemp = phase.cutoff_temp; - if (phase5CutoffTemp < phase4CutoffTemp) { + if (phase5CutoffTemp < phase4CutoffTemp) + { phaseCufoffTempErrorMesgBox(5); return; } @@ -514,7 +581,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { if (!phaseCufoffTempAndTempFlowCheck(5, phase4CutoffTemp, phase5CutoffTemp, - phase.temp_flow)) { + phase.temp_flow)) + { return; } } @@ -522,18 +590,20 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { { Phase phase; - phase.onoff = ui->checkBox_phase_6->checkState() ? 1 : 0; - phase.cutoff_temp = ui->phase_6_cutoff_temp->text().toFloat(); - phase.temp_flow = ui->phase_6_scan_rate->text().toFloat(); + phase.onoff = ui->checkBox_phase_6->checkState() ? 1 : 0; + phase.cutoff_temp = ui->phase_6_cutoff_temp->text().toFloat(); + phase.temp_flow = ui->phase_6_scan_rate->text().toFloat(); phase.constant_temp_time_min = (uint16_t)ui->phase_6_constant_temp->text().toInt(); - phase.gas = conver2GasType(ui->comboBox_phase_6_atmosphere->currentIndex()); + phase.gas = conver2GasType(ui->comboBox_phase_6_atmosphere->currentIndex()); phaseVtr.push_back(phase); // - if (phase.onoff) { + if (phase.onoff) + { logde << "phase 6 enable."; phase6CutoffTemp = phase.cutoff_temp; - if (phase6CutoffTemp < phase5CutoffTemp) { + if (phase6CutoffTemp < phase5CutoffTemp) + { phaseCufoffTempErrorMesgBox(6); return; } @@ -546,17 +616,18 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { if (!phaseCufoffTempAndTempFlowCheck(6, phase5CutoffTemp, phase6CutoffTemp, - phase.temp_flow)) { + phase.temp_flow)) + { return; } } } // Global::ExperimentInfo &ei = Global::_experimentInfo; - ei.sampleName = ui->sampleNameLineEdit->text(); - ei.sampleWeight = ui->sampleWeightLineEdit->text(); - ei.date = ui->dateTimeLineEdit->text(); - ei.experimentor = ui->userLineEdit->text(); + ei.sampleName = ui->sampleNameLineEdit->text(); + ei.sampleWeight = ui->sampleWeightLineEdit->text(); + ei.date = ui->dateTimeLineEdit->text(); + ei.experimentor = ui->userLineEdit->text(); #if 0 ei.phaseVtr = phaseVtr; @@ -571,13 +642,16 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { // initial data. QByteArray initialBa; - if (ui->radioButton_OIT->isChecked()) { + if (ui->radioButton_OIT->isChecked()) + { initialBa.append((char)0); Global::_experimentOITFlag = true; logde << "Global::_experimentOITFlag = true;"; - } else { + } + else + { initialBa.append((char)1); Global::_experimentOITFlag = false; @@ -589,7 +663,8 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { initialBa.append(index); // 实验名称检查 - if (Global::isFileExist(ei.sampleName)) { + if (Global::isFileExist(ei.sampleName)) + { QMessageBox::question(this, "实验名称重复", "实验名称已存在,请重新输入!", QMessageBox::Yes); return; @@ -603,55 +678,73 @@ void ExperimentSettingForm::on_pushButton_deliverData_clicked() { hide(); } -void ExperimentSettingForm::slotCancel() { +void ExperimentSettingForm::slotCancel() +{ hide(); } -void ExperimentSettingForm::phaseCufoffTempErrorMesgBox(const int phaseNum) { +void ExperimentSettingForm::phaseCufoffTempErrorMesgBox(const int phaseNum) +{ QString title = QString("阶段%1截止温度错误").arg(QString::number(phaseNum)); - QString text = QString("阶段%1截止温度不能小于上一阶段截止温度! ").arg(QString::number(phaseNum)); + QString text = QString("阶段%1截止温度不能小于上一阶段截止温度! ").arg(QString::number(phaseNum)); QMessageBox::question(this, title, text, QMessageBox::Yes); } -void ExperimentSettingForm::phaseScanRateErrorMesgBox(const int phaseNum) { +void ExperimentSettingForm::phaseScanRateErrorMesgBox(const int phaseNum) +{ QString title = QString("阶段%1扫描速率错误").arg(QString::number(phaseNum)); - QString text = QString("阶段%1扫描速率必须大于0! ").arg(QString::number(phaseNum)); + QString text = QString("阶段%1扫描速率必须大于0! ").arg(QString::number(phaseNum)); QMessageBox::question(this, title, text, QMessageBox::Yes); } -void ExperimentSettingForm::slotPhaseCheck() { - if (sender() == ui->checkBox_phase_2) { - if (ui->checkBox_phase_2->isChecked()) { +void ExperimentSettingForm::slotPhaseCheck() +{ + if (sender() == ui->checkBox_phase_2) + { + if (ui->checkBox_phase_2->isChecked()) + { ui->phase_2_cutoff_temp->setEnabled(true); ui->phase_2_scan_rate->setEnabled(true); ui->phase_2_constant_temp->setEnabled(true); ui->comboBox_phase_2_atmosphere->setEnabled(true); - } else { + } + else + { ui->phase_2_cutoff_temp->setEnabled(false); ui->phase_2_scan_rate->setEnabled(false); ui->phase_2_constant_temp->setEnabled(false); ui->comboBox_phase_2_atmosphere->setEnabled(false); } - } else if (sender() == ui->checkBox_phase_3) { - if (ui->checkBox_phase_3->isChecked()) { + } + else if (sender() == ui->checkBox_phase_3) + { + if (ui->checkBox_phase_3->isChecked()) + { ui->phase_3_cutoff_temp->setEnabled(true); ui->phase_3_scan_rate->setEnabled(true); ui->phase_3_constant_temp->setEnabled(true); ui->comboBox_phase_3_atmosphere->setEnabled(true); - } else { + } + else + { ui->phase_3_cutoff_temp->setEnabled(false); ui->phase_3_scan_rate->setEnabled(false); ui->phase_3_constant_temp->setEnabled(false); ui->comboBox_phase_3_atmosphere->setEnabled(false); } - } else if (sender() == ui->checkBox_phase_4) { - if (ui->checkBox_phase_4->isChecked()) { + } + else if (sender() == ui->checkBox_phase_4) + { + if (ui->checkBox_phase_4->isChecked()) + { ui->phase_4_cutoff_temp->setEnabled(true); ui->phase_4_scan_rate->setEnabled(true); ui->phase_4_constant_temp->setEnabled(true); ui->comboBox_phase_4_atmosphere->setEnabled(true); - } else { + } + else + { ui->phase_4_cutoff_temp->setEnabled(false); ui->phase_4_scan_rate->setEnabled(false); ui->phase_4_constant_temp->setEnabled(false); @@ -660,23 +753,25 @@ void ExperimentSettingForm::slotPhaseCheck() { } } -void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { +void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) +{ logde << "recv phase info ----------------------- "; #if 1 - QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 + QString hexData = ba.toHex(' '); // ' ' 作为分隔符,可选参数 qDebug() << "slotRecvPhaseInfo hex:" << hexData; #endif - QByteArray localba = ba; - SerialPortProtocol *spp = (SerialPortProtocol *)localba.data(); + QByteArray localba = ba; + SerialPortProtocol *spp = (SerialPortProtocol *)localba.data(); u8 *data = spp->data_buf; int phaseSize = spp->len / PHASE_BYTE_SIZE; QVector phaseVtr; - for (int i = 0; i < phaseSize; i++) { + for (int i = 0; i < phaseSize; i++) + { #if 0 Phase *phase = (Phase *)(data + i * PHASE_BYTE_SIZE); phaseVtr.push_back(*phase); @@ -719,7 +814,7 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { phaseVtr.push_back(phase); #endif uint8_t *localData = data + i * PHASE_BYTE_SIZE; - int index = 0; + int index = 0; Phase phase; // 解析 onoff @@ -741,19 +836,20 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { // 解析 gas uint8_t gasType = *(localData + index); - switch (gasType) { - case NC: - phase.gas = NC; - break; - case N2: - phase.gas = N2; - break; - case O2: - phase.gas = O2; - break; - default: - phase.gas = NC; // 默认值,表示未知类型 - break; + switch (gasType) + { + case NC: + phase.gas = NC; + break; + case N2: + phase.gas = N2; + break; + case O2: + phase.gas = O2; + break; + default: + phase.gas = NC; // 默认值,表示未知类型 + break; } phaseVtr.push_back(phase); } @@ -763,51 +859,62 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { // ui update // logde<<"phaseVtr size:"<(this->findChild(checkBoxName)); - if (checkBox_phase) { + if (checkBox_phase) + { checkBox_phase->setChecked(true); // qDebug()<<"found..."; - } else { + } + else + { // qDebug()<<"not found..."; } - QString cutOffTempLineEditName = QString("phase_%1_cutoff_temp").arg(i); - QLineEdit *cutOffTempLineEdit = qobject_cast(this->findChild(cutOffTempLineEditName)); - if (cutOffTempLineEdit) { + QString cutOffTempLineEditName = QString("phase_%1_cutoff_temp").arg(i); + QLineEdit *cutOffTempLineEdit = qobject_cast(this->findChild(cutOffTempLineEditName)); + if (cutOffTempLineEdit) + { cutOffTempLineEdit->setText(QString::number(phase.cutoff_temp, 'f', 3)); cutOffTempLineEdit->setEnabled(true); } - QString scanRateLineEditName = QString("phase_%1_scan_rate").arg(i); + QString scanRateLineEditName = QString("phase_%1_scan_rate").arg(i); QLineEdit *scanRateLineEdit = qobject_cast(this->findChild(scanRateLineEditName)); - if (scanRateLineEdit) { + if (scanRateLineEdit) + { scanRateLineEdit->setText(QString::number(phase.temp_flow, 'f', 3)); scanRateLineEdit->setEnabled(true); } - QString constantTempLineEditName = QString("phase_%1_constant_temp").arg(i); + QString constantTempLineEditName = QString("phase_%1_constant_temp").arg(i); QLineEdit *constantTempLineEdit = qobject_cast(this->findChild(constantTempLineEditName)); - if (constantTempLineEdit) { + if (constantTempLineEdit) + { constantTempLineEdit->setText(QString::number(phase.constant_temp_time_min)); constantTempLineEdit->setEnabled(true); } - QString atmoshpereComboBoxName = QString("comboBox_phase_%1_atmosphere").arg(i); + QString atmoshpereComboBoxName = QString("comboBox_phase_%1_atmosphere").arg(i); QComboBox *atmoshpereComboBox = qobject_cast(this->findChild(atmoshpereComboBoxName)); - if (atmoshpereComboBox) { + if (atmoshpereComboBox) + { atmoshpereComboBox->setCurrentIndex(phase.gas % 256); atmoshpereComboBox->setEnabled(true); } @@ -817,10 +924,13 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { // radioButton_OIT // comboBox_initial_atmosphere + // 设置OIT类型 u8 *oitValue = (data + 72); + logde << "oit value:" << (int)*oitValue; - if (*oitValue) { + if (*oitValue) + { ui->radioButton_OIT->setChecked(false); ui->radioButton_OIT_not->setChecked(true); #if 0 @@ -831,7 +941,9 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { oitRadioButton->setChecked(true); } #endif - } else { + } + else + { ui->radioButton_OIT->setChecked(true); ui->radioButton_OIT_not->setChecked(false); Global::_experimentOITFlag = true; @@ -847,6 +959,7 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { #endif } + // 设置默认其他类型。 u8 *initAtmosphereValue = (data + 73); logde << "initAtmosphereValue:" << (int)(*initAtmosphereValue); ui->comboBox_initial_atmosphere->setCurrentIndex((int)*initAtmosphereValue); @@ -861,9 +974,28 @@ void ExperimentSettingForm::slotRecvPhaseInfo(const QByteArray &ba) { #endif } -void ExperimentSettingForm::showEvent(QShowEvent *event) { - QDateTime currentDateTime = QDateTime::currentDateTime(); - QString internationalFormat = currentDateTime.toString("yyyy/MM/dd hh:mm:ss"); +void ExperimentSettingForm::showEvent(QShowEvent *event) +{ + QDateTime currentDateTime = QDateTime::currentDateTime(); + QString internationalFormat = currentDateTime.toString("yyyy/MM/dd hh:mm:ss"); ui->dateTimeLineEdit->setText(internationalFormat); + + // 测试类型OIT设置 + if (Global::_experimentInfo.testType == Global::TestType::OIT) + { + Global::_experimentOITFlag = true; + + ui->radioButton_OIT->setChecked(true); + ui->radioButton_OIT_not->setChecked(false); + } + else + { + Global::_experimentOITFlag = false; + + ui->radioButton_OIT->setChecked(false); + ui->radioButton_OIT_not->setChecked(true); + } + // 初始气氛 + ui->comboBox_initial_atmosphere->setCurrentIndex((int)Global::_experimentInfo.initialAtmosPhere); } diff --git a/src/ui/leftwidget.cpp b/src/ui/leftwidget.cpp index 787d248..fe183c6 100644 --- a/src/ui/leftwidget.cpp +++ b/src/ui/leftwidget.cpp @@ -15,7 +15,7 @@ #include "leftwidget.h" #include "filemanager.h" #include "global.h" -#include "logger.h" +#include "../logger/logger.h" LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent) { @@ -38,8 +38,13 @@ LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent) _contextMenu = new QMenu(_treeWidget); _deleteFileAction = new QAction("删除文件", this); _createFolderAction = new QAction("新建文件夹", this); + _moveToFolderAction = new QAction("移动到文件夹", this); + _moveToFolderSubMenu = new QMenu("移动到文件夹", this); // Initialize submenu + _moveToFolderAction->setMenu(_moveToFolderSubMenu); // Set submenu for move to folder action + _contextMenu->addAction(_deleteFileAction); _contextMenu->addAction(_createFolderAction); + _contextMenu->addMenu(_moveToFolderSubMenu); // Add submenu instead of action _treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); @@ -50,20 +55,112 @@ LeftWidget::LeftWidget(QWidget *parent) : QDockWidget(parent) this, &LeftWidget::slotDeleteActionTriggered); connect(_createFolderAction, &QAction::triggered, this, &LeftWidget::slotCreateFolderActionTriggered); + connect(_moveToFolderAction, &QAction::triggered, + this, &LeftWidget::slotMoveToFolderActionTriggered); // Connect the move to folder action +} + +void LeftWidget::slotShowContextMenu(const QPoint &pos) +{ + QTreeWidgetItem *item = _treeWidget->itemAt(pos); + if (item) + { + // 获取文件路径 + QString filePath = item->data(0, Qt::UserRole).toString(); + QString dirPath = item->data(0, Qt::UserRole + 1).toString(); + + logde << "filePath:" << filePath.toStdString(); + logde << "dirPath:" << dirPath.toStdString(); + + // 如果是文件 + if (!filePath.isEmpty()) + { + _deleteFileActionFilePath = filePath; + _deleteFileAction->setVisible(true); + _deleteFileAction->setText(tr("删除文件")); + + // 显示移动到文件夹选项 + _moveToFolderAction->setVisible(true); + + // 清除之前的子菜单项 + _moveToFolderSubMenu->clear(); + + // 获取根目录下的文件夹路径并创建子菜单项(只显示第一层文件夹) + QStringList folderPaths = getRootFolderPaths(); + for (const QString &folderPath : folderPaths) + { + // 获取文件夹名称 + QString folderName = QFileInfo(folderPath).fileName(); + + // 创建子菜单项 + QAction *folderAction = new QAction(folderName, this); + folderAction->setData(folderPath); // 将完整路径存储在action中 + + // 连接信号到槽 + connect(folderAction, &QAction::triggered, this, [this, folderAction]() + { slotMoveToSpecificFolder(folderAction->data().toString()); }); + + // 添加到子菜单 + _moveToFolderSubMenu->addAction(folderAction); + } + + // 如果没有可用文件夹,禁用移动功能 + if (folderPaths.isEmpty()) + { + _moveToFolderAction->setEnabled(false); + } + else + { + _moveToFolderAction->setEnabled(true); + } + } + // 如果是目录 + else if (!dirPath.isEmpty()) + { + // 不显示删除文件功能 + _deleteFileAction->setVisible(false); + + // 不显示移动到文件夹功能(目录不能移动) + _moveToFolderAction->setVisible(false); + _moveToFolderAction->setEnabled(false); + +#if 0 + _deleteFileAction->setVisible(true); + _deleteFileActionFilePath = dirPath; + _deleteFileAction->setText(tr("删除文件夹")); +#endif + } + else + { + // 对于根节点或其他项目,不显示移动功能 + _moveToFolderAction->setVisible(false); + _moveToFolderAction->setEnabled(false); + } + + // 只有当点击的是目录时才显示"新建文件夹"选项 + bool isDir = !dirPath.isEmpty(); + _createFolderAction->setVisible(isDir); + + _contextMenu->exec(_treeWidget->mapToGlobal(pos)); + } } void LeftWidget::reloadFileName() { +#if 0 clearAllChildItems(_sampleDataItem); // clearAllChildItems(_baseLineItem); clearAllChildItems(_analysisStateItem); initFileName(_sampleDataItem, Global::SampleDataFloder); + #if 0 initFileName(_baseLineItem,Global::BaseLineFolder); #endif initFileName(_analysisStateItem, Global::AnalysisStateFolder); +#endif + + reloadFileTree(); // 更新文件列表 Global::updateFileList(); } @@ -131,6 +228,7 @@ void LeftWidget::initData() } #endif } + void LeftWidget::slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column) { qDebug() << "item clicked:" << item->text(0) << column; @@ -237,80 +335,54 @@ void LeftWidget::recursiveFolderOperation(const QString& folderPath) { } #endif -void LeftWidget::slotShowContextMenu(const QPoint &pos) -{ - QTreeWidgetItem *item = _treeWidget->itemAt(pos); - if (item) { - // 获取文件路径 - QString filePath = item->data(0, Qt::UserRole).toString(); - QString dirPath = item->data(0, Qt::UserRole + 1).toString(); - - // 如果是文件 - if (!filePath.isEmpty()) { - _deleteFileActionFilePath = filePath; - _deleteFileAction->setVisible(true); - _deleteFileAction->setText(tr("删除文件")); - } - // 如果是目录 - else if (!dirPath.isEmpty()) { - // 暂时不要删除文件件功能 - _deleteFileAction->setVisible(false); - #if 0 - _deleteFileAction->setVisible(true); - _deleteFileActionFilePath = dirPath; - _deleteFileAction->setText(tr("删除文件夹")); - #endif - } else { - _deleteFileAction->setVisible(false); - } - - // 只有当点击的是目录时才显示"新建文件夹"选项 - bool isDir = !dirPath.isEmpty(); - _createFolderAction->setVisible(isDir); - - _contextMenu->exec(_treeWidget->mapToGlobal(pos)); - } -} - void LeftWidget::slotDeleteActionTriggered() { - if (_deleteFileActionFilePath.isEmpty()) { + if (_deleteFileActionFilePath.isEmpty()) + { return; } - + QFileInfo fileInfo(_deleteFileActionFilePath); bool isDir = fileInfo.isDir(); - + // 检查文件/目录是否存在 - if (!fileInfo.exists()) { + if (!fileInfo.exists()) + { QMessageBox::warning(this, tr("警告"), tr("文件/目录不存在!")); return; } - + // 确认删除对话框 - int ret = QMessageBox::question(this, tr("确认删除"), - tr("确定要删除%1\n%2?").arg(isDir ? tr("文件夹及其所有内容") : tr("文件")).arg(_deleteFileActionFilePath), - QMessageBox::Yes | QMessageBox::No, - QMessageBox::No); - - if (ret == QMessageBox::Yes) { + int ret = QMessageBox::question(this, tr("确认删除"), + tr("确定要删除%1\n%2?").arg(isDir ? tr("文件夹及其所有内容") : tr("文件")).arg(_deleteFileActionFilePath), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + + if (ret == QMessageBox::Yes) + { bool success; - if (isDir) { + if (isDir) + { // 删除目录及其内容 success = removeDir(_deleteFileActionFilePath); - } else { + } + else + { // 删除文件 success = QFile::remove(_deleteFileActionFilePath); } - - if (success) { + + if (success) + { // 重新加载文件树 reloadFileTree(); - } else { + } + else + { QMessageBox::critical(this, tr("错误"), tr("删除失败!")); } } - + _deleteFileActionFilePath.clear(); } @@ -318,83 +390,96 @@ bool LeftWidget::removeDir(const QString &dirPath) { bool result = true; QDir dir(dirPath); - - if (dir.exists(dirPath)) { - Q_FOREACH(QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) { - if (info.isDir()) { + + if (dir.exists(dirPath)) + { + Q_FOREACH (QFileInfo info, dir.entryInfoList(QDir::NoDotAndDotDot | QDir::System | QDir::Hidden | QDir::AllDirs | QDir::Files, QDir::DirsFirst)) + { + if (info.isDir()) + { result = removeDir(info.absoluteFilePath()); - } else { + } + else + { result = QFile::remove(info.absoluteFilePath()); } - - if (!result) { + + if (!result) + { return result; } } result = dir.rmdir(dirPath); } - + return result; } void LeftWidget::slotCreateFolderActionTriggered() { - // 获取当前选中的项目 - QTreeWidgetItem *currentItem = _treeWidget->currentItem(); - if (!currentItem) { - return; - } - + // 创建主分支下的文件夹 + QTreeWidgetItem *currentItem = _treeWidget->topLevelItem(0); + logde << "currentItem : " << currentItem->text(0).toStdString(); + // 获取当前目录路径 QString dirPath; - if (currentItem->data(0, Qt::UserRole + 1).toString().isEmpty()) { + if (currentItem->data(0, Qt::UserRole + 1).toString().isEmpty()) + { // 如果是文件,获取其父目录 dirPath = QFileInfo(currentItem->data(0, Qt::UserRole).toString()).absolutePath(); - } else { + } + else + { // 如果是目录,直接使用其路径 dirPath = currentItem->data(0, Qt::UserRole + 1).toString(); } - + // 弹出对话框,输入新文件夹名称 bool ok; - QString folderName = QInputDialog::getText(this, tr("新建文件夹"), - tr("请输入文件夹名称:"), - QLineEdit::Normal, + QString folderName = QInputDialog::getText(this, tr("新建文件夹"), + tr("请输入文件夹名称:"), + QLineEdit::Normal, tr("新建文件夹"), &ok); - - if (!ok || folderName.isEmpty()) { + + if (!ok || folderName.isEmpty()) + { return; } - + // 检查文件夹名称是否有效 - if (folderName.contains(QRegExp("[\\/:*?\"<>|]"))) { + if (folderName.contains(QRegExp("[\\/:*?\"<>|]"))) + { QMessageBox::warning(this, tr("错误"), tr("文件夹名称包含非法字符!")); return; } - + // 创建新文件夹路径 QString newFolderPath = dirPath + "/" + folderName; - + // 检查文件夹是否已存在 - if (QDir(newFolderPath).exists()) { + if (QDir(newFolderPath).exists()) + { QMessageBox::warning(this, tr("错误"), tr("文件夹已存在!")); return; } - + // 创建文件夹 - if (QDir().mkdir(newFolderPath)) { + if (QDir().mkdir(newFolderPath)) + { // 保存当前展开状态 saveExpandedState(); - + // 重新加载文件树 reloadFileTree(); - + // 恢复展开状态 restoreExpandedState(); - + // 展开当前目录并选中新创建的文件夹 expandAndSelectItem(newFolderPath); - } else { + } + else + { QMessageBox::critical(this, tr("错误"), tr("创建文件夹失败!")); } } @@ -403,18 +488,21 @@ void LeftWidget::expandAndSelectItem(const QString &path) { // 递归展开到指定路径并选中该项 QTreeWidgetItemIterator it(_treeWidget); - while (*it) { + while (*it) + { QTreeWidgetItem *item = *it; QString itemPath = item->data(0, Qt::UserRole + 1).toString(); - - if (itemPath == path) { + + if (itemPath == path) + { // 展开所有父节点 QTreeWidgetItem *parent = item->parent(); - while (parent) { + while (parent) + { parent->setExpanded(true); parent = parent->parent(); } - + // 选中新创建的文件夹 _treeWidget->setCurrentItem(item); break; @@ -446,26 +534,26 @@ void LeftWidget::reloadFileTree() { // 保存当前展开状态 saveExpandedState(); - + // 清空现有的树形结构 _treeWidget->clear(); - + // 创建根节点 QTreeWidgetItem *rootItem = new QTreeWidgetItem(_treeWidget); rootItem->setText(0, "实验数据"); - rootItem->setData(0, Qt::UserRole, Global::ExperimentDirPath); + // rootItem->setData(0, Qt::UserRole, Global::ExperimentDirPath); // 保存根目录路径,用于恢复展开状态 rootItem->setData(0, Qt::UserRole + 1, Global::ExperimentDirPath); - + // 递归扫描目录并构建树形结构 buildFileTree(rootItem, Global::ExperimentDirPath); - + // 展开根节点 rootItem->setExpanded(true); - + // 恢复展开状态 restoreExpandedState(); - + // 更新全局文件列表 Global::updateFileList(); } @@ -487,39 +575,47 @@ QFileInfoList LeftWidget::scanDirRecursively(const QString &rootPath) void LeftWidget::buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPath) { QDir dir(dirPath); - if (!dir.exists()) { + if (!dir.exists()) + { qWarning() << "目录不存在: " << dirPath; return; } - + // 获取所有子目录和文件,按名称排序 dir.setSorting(QDir::Name | QDir::DirsFirst); QFileInfoList list = dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); - - for (const QFileInfo &fileInfo : list) { + + for (const QFileInfo &fileInfo : list) + { QTreeWidgetItem *item = new QTreeWidgetItem(parentItem); - - if (fileInfo.isDir()) { + + if (fileInfo.isDir()) + { // 设置目录项 item->setText(0, fileInfo.fileName()); // 使用系统默认的目录图标 item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_DirIcon)); // 保存目录路径,用于恢复展开状态 item->setData(0, Qt::UserRole + 1, fileInfo.absoluteFilePath()); - + // 递归处理子目录 buildFileTree(item, fileInfo.absoluteFilePath()); - } else { + } + else + { // 设置文件项 item->setText(0, fileInfo.fileName()); item->setData(0, Qt::UserRole, fileInfo.absoluteFilePath()); - + // 根据文件类型设置不同的图标 QString suffix = fileInfo.suffix().toLower(); - if (suffix == "xlsx" || suffix == "xls") { + if (suffix == "xlsx" || suffix == "xls") + { // 使用系统默认的文档图标 item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon)); - } else { + } + else + { // 使用默认文件图标 item->setIcon(0, _treeWidget->style()->standardIcon(QStyle::SP_FileIcon)); } @@ -530,41 +626,179 @@ void LeftWidget::buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPa void LeftWidget::saveExpandedState() { _expandedPaths.clear(); - + // 递归遍历所有项,保存展开的目录路径 QTreeWidgetItemIterator it(_treeWidget); - while (*it) { + while (*it) + { QTreeWidgetItem *item = *it; - if (item->isExpanded()) { + if (item->isExpanded()) + { QVariant pathData = item->data(0, Qt::UserRole + 1); - if (!pathData.isNull()) { + if (!pathData.isNull()) + { _expandedPaths.append(pathData.toString()); } } ++it; } - + qDebug() << "保存了" << _expandedPaths.size() << "个展开状态:" << _expandedPaths; } void LeftWidget::restoreExpandedState() { int restoredCount = 0; - + // 递归遍历所有项,恢复展开状态 QTreeWidgetItemIterator it(_treeWidget); - while (*it) { + while (*it) + { QTreeWidgetItem *item = *it; QVariant pathData = item->data(0, Qt::UserRole + 1); - if (!pathData.isNull()) { + if (!pathData.isNull()) + { QString path = pathData.toString(); - if (_expandedPaths.contains(path)) { + if (_expandedPaths.contains(path)) + { item->setExpanded(true); restoredCount++; } } ++it; } - + qDebug() << "恢复了" << restoredCount << "个展开状态"; } + +void LeftWidget::slotMoveToFolderActionTriggered() +{ + // 获取当前选中的项目 + QTreeWidgetItem *currentItem = _treeWidget->currentItem(); + if (!currentItem) + { + return; + } + + // 检查是否是文件(不能移动文件夹) + QString filePath = currentItem->data(0, Qt::UserRole).toString(); + if (filePath.isEmpty()) + { + QMessageBox::information(this, tr("提示"), tr("请选择一个文件进行移动!")); + return; + } + + // 弹出对话框让用户选择目标文件夹 + QStringList folderPaths = getAllFolderPaths(); + + bool ok; + QString selectedPath = QInputDialog::getItem(this, + tr("选择目标文件夹"), + tr("请选择要移动到的目标文件夹:"), + folderPaths, + 0, + false, + &ok); + + if (!ok || selectedPath.isEmpty()) + { + return; + } + + slotMoveToSpecificFolder(selectedPath); +} + +void LeftWidget::slotMoveToSpecificFolder(const QString &targetFolderPath) +{ + // 获取当前选中的项目 + QTreeWidgetItem *currentItem = _treeWidget->currentItem(); + if (!currentItem) + { + return; + } + + // 检查是否是文件(不能移动文件夹) + QString filePath = currentItem->data(0, Qt::UserRole).toString(); + if (filePath.isEmpty()) + { + QMessageBox::information(this, tr("提示"), tr("请选择一个文件进行移动!")); + return; + } + + // 保存要移动的文件路径 + QString fileToMove = filePath; + + // 执行移动操作 + QString targetPath = targetFolderPath + "/" + QFileInfo(fileToMove).fileName(); + + // 检查目标文件是否已存在 + if (QFile::exists(targetPath)) + { + int ret = QMessageBox::question(this, tr("确认覆盖"), + tr("目标文件已存在,是否覆盖?\n%1").arg(targetPath), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No); + if (ret == QMessageBox::No) + { + return; + } + } + + // 移动文件 + if (QFile::rename(fileToMove, targetPath)) + { + logde << "文件移动成功: " << fileToMove.toStdString() << " -> " << targetPath.toStdString(); + + // 保存当前展开状态 + saveExpandedState(); + // 重新加载文件树 + reloadFileTree(); + // 恢复展开状态 + restoreExpandedState(); + + // 展开并选中目标文件夹 + expandAndSelectItem(targetFolderPath); + } + else + { + QMessageBox::critical(this, tr("错误"), tr("文件移动失败!")); + } +} + +QStringList LeftWidget::getRootFolderPaths() +{ + QStringList folderPaths; + + QDir rootDir(Global::ExperimentDirPath); + if (rootDir.exists()) + { + // Get only directories in the root path (no recursion) + QFileInfoList entries = rootDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QFileInfo &entry : entries) + { + folderPaths << entry.absoluteFilePath(); + } + } + + return folderPaths; +} + +QStringList LeftWidget::getAllFolderPaths() +{ + QStringList folderPaths; + + // 递归遍历所有项目以获取所有文件夹路径 + QTreeWidgetItemIterator it(_treeWidget); + while (*it) + { + QTreeWidgetItem *item = *it; + QString dirPath = item->data(0, Qt::UserRole + 1).toString(); + if (!dirPath.isEmpty()) + { + folderPaths << dirPath; + } + ++it; + } + + return folderPaths; +} diff --git a/src/ui/leftwidget.h b/src/ui/leftwidget.h index 263bf62..44b674d 100644 --- a/src/ui/leftwidget.h +++ b/src/ui/leftwidget.h @@ -11,7 +11,7 @@ #include "global.h" -class LeftWidget:public QDockWidget +class LeftWidget : public QDockWidget { Q_OBJECT public: @@ -21,44 +21,56 @@ public: void reloadFileTree(); - QString filePathCheck(const QString fileName,const QString folderPath); + QString filePathCheck(const QString fileName, const QString folderPath); + // 确认删除文件 void confirmDelete(bool enabled); signals: - void sigSendAnalysisFileName(const QString&); - void sigDeleteActionTriggered(const QString&); + void sigSendAnalysisFileName(const QString &); + void sigDeleteActionTriggered(const QString &); + private: void initData(); - void initFileName(QTreeWidgetItem*,const QString &folderPath); - void expandAll(QTreeWidgetItem* item); - void clearAllChildItems(QTreeWidgetItem* parentItem); + void initFileName(QTreeWidgetItem *, const QString &folderPath); + void expandAll(QTreeWidgetItem *item); + void clearAllChildItems(QTreeWidgetItem *parentItem); QFileInfoList scanDirRecursively(const QString &rootPath); void buildFileTree(QTreeWidgetItem *parentItem, const QString &dirPath); void saveExpandedState(); void restoreExpandedState(); void expandAndSelectItem(const QString &path); bool removeDir(const QString &dirPath); + QStringList getAllFolderPaths(); // Add function to get all folder paths for move operation + QStringList getRootFolderPaths(); // Add function to get only root level folder paths private slots: void slotTreeWidgetItemClicked(QTreeWidgetItem *item, int column); + // 右键菜单 void slotShowContextMenu(const QPoint &pos); void slotDeleteActionTriggered(); void slotCreateFolderActionTriggered(); + void slotMoveToFolderActionTriggered(); // Add slot for move to folder action + void slotMoveToSpecificFolder(const QString &targetFolderPath); // 新增:移动到指定文件夹的槽函数 + private: QTreeWidget *_treeWidget; - + +#if 0 QTreeWidgetItem *_analysisStateItem, - *_baseLineItem, - *_sampleDataItem; + *_baseLineItem, + *_sampleDataItem; +#endif QMenu *_contextMenu; - + QAction *_deleteFileAction; QAction *_createFolderAction; + QAction *_moveToFolderAction; + QMenu *_moveToFolderSubMenu; // Add submenu for move to folder + QString _deleteFileActionFilePath; - + // 保存展开状态的列表 QStringList _expandedPaths; }; -#endif // LEFTWIDGET_H - +#endif // LEFTWIDGET_H \ No newline at end of file diff --git a/src/ui/printpreviewform.cpp b/src/ui/printpreviewform.cpp index de1d9eb..17b1ae0 100644 --- a/src/ui/printpreviewform.cpp +++ b/src/ui/printpreviewform.cpp @@ -84,7 +84,6 @@ void PrintPreviewForm::slotPaintRequested(QPrinter *printer) { painter.setOpacity(1.0); #endif - // 获取页面矩形区域 QRect pageRect = printer->pageRect();