From 7d13b777cec0f55bb49c9e8ad8e92463c5870f75 Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Tue, 19 Aug 2025 08:30:48 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86=E6=89=93?= =?UTF-8?q?=E5=8C=85=E5=AF=B9socket=E6=A8=A1=E5=BC=8F=E7=9A=84=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 19 ++++ backend/app.spec | 3 +- backend/build_app.py | 3 +- backend/devices/utils/config_manager.py | 12 +- backend/main.py | 10 +- backend/tests/moniter_windows.py | 66 +++++++++++ backend/tests/testfemtobolt.py | 104 ++++++++++++++++++ .../resources/backend/Log/OrbbecSDK.log.txt | 20 ---- .../dll/smitsense/SMiTSenseUsbWrapper.dll | Bin 18944 -> 0 bytes .../resources/backend/start_backend.bat | 17 --- frontend/src/renderer/src/views/Detection.vue | 10 +- .../src/renderer/src/views/PatientCreate.vue | 7 +- 12 files changed, 221 insertions(+), 50 deletions(-) create mode 100644 backend/tests/moniter_windows.py create mode 100644 backend/tests/testfemtobolt.py delete mode 100644 frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt delete mode 100644 frontend/src/renderer/dist-electron/win-unpacked/resources/backend/dll/smitsense/SMiTSenseUsbWrapper.dll delete mode 100644 frontend/src/renderer/dist-electron/win-unpacked/resources/backend/start_backend.bat diff --git a/.gitignore b/.gitignore index 3cb09131..2e4afe99 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,10 @@ Thumbs.db dist/ build/ +# 前端构建输出 +frontend/src/renderer/dist/ +frontend/src/renderer/dist-electron/ + # 临时文件 *.tmp *.temp @@ -21394,3 +21398,18 @@ frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBa frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/_internal/werkzeug-2.3.7.dist-info/WHEEL frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/config.ini +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/depthengine_2_0.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/k4a.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/k4arecord.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/live555.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/ob_usb.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/OrbbecSDK.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/lib/k4a.lib +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/femtobolt/lib/k4arecord.lib +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/smitsense/SMiTSenseUsb-F3.0.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/smitsense/SMiTSenseUsb-F3.0.lib +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/smitsense/SMiTSenseUsb-F3.0d.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/smitsense/SMiTSenseUsbWrapper.dll +frontend/src/renderer/dist-electron/win-unpacked/resources/backend/BodyBalanceBackend/dll/smitsense/Wrapper.dll diff --git a/backend/app.spec b/backend/app.spec index 0997eb73..9d7bb7fe 100644 --- a/backend/app.spec +++ b/backend/app.spec @@ -14,7 +14,8 @@ a = Analysis( ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 - ('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 + ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # Wrapper + ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # Wrapper ], hiddenimports=[ 'flask', diff --git a/backend/build_app.py b/backend/build_app.py index 4bda99e1..9a60aee8 100644 --- a/backend/build_app.py +++ b/backend/build_app.py @@ -42,7 +42,8 @@ a = Analysis( ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 - ('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 + ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # Wrapper + ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # Wrapper ], hiddenimports=[ 'flask', diff --git a/backend/devices/utils/config_manager.py b/backend/devices/utils/config_manager.py index 28f8a211..68df176e 100644 --- a/backend/devices/utils/config_manager.py +++ b/backend/devices/utils/config_manager.py @@ -11,7 +11,7 @@ import json import logging from typing import Dict, Any, Optional, Union from pathlib import Path - +import sys class ConfigManager: """配置管理器""" @@ -45,9 +45,15 @@ class ConfigManager: """ # 可能的配置文件路径 possible_paths = [ - os.path.join(os.path.dirname(__file__), 'config.ini') + # os.path.join(os.path.dirname(__file__), 'config.ini') ] - + # 配置文件路径 + if getattr(sys, 'frozen', False): + # 打包后的可执行文件 + possible_paths.append(os.path.join(os.path.dirname(sys.executable), 'config.ini')) + else: + # 开发环境 + possible_paths.append(os.path.join(os.path.dirname(__file__), 'config.ini')) for path in possible_paths: abs_path = os.path.abspath(path) if os.path.exists(abs_path): diff --git a/backend/main.py b/backend/main.py index 902e9407..2b7eef5f 100644 --- a/backend/main.py +++ b/backend/main.py @@ -129,6 +129,10 @@ class AppServer: logging.StreamHandler() ] ) + + # 设置werkzeug日志级别为WARNING,过滤掉INFO级别的访问日志 + logging.getLogger('werkzeug').setLevel(logging.WARNING) + self.logger = logging.getLogger(__name__) def _init_socketio(self): @@ -143,10 +147,10 @@ class AppServer: engineio_logger=False, ping_timeout=60, ping_interval=25, - manage_session=True, # 启用会话管理,解决打包环境会话问题 + manage_session=False, always_connect=False, - transports=['polling'], # 只使用polling,避免打包环境websocket问题 - allow_upgrades=False, # 禁用升级到websocket + transports=['polling', 'websocket'], # 优先使用polling + allow_upgrades=True, # 允许升级到websocket cookie=None # 禁用cookie ) self.logger.info('SocketIO初始化成功') diff --git a/backend/tests/moniter_windows.py b/backend/tests/moniter_windows.py new file mode 100644 index 00000000..4198c2b1 --- /dev/null +++ b/backend/tests/moniter_windows.py @@ -0,0 +1,66 @@ +import psutil +import time +from datetime import datetime +from pynvml import * + +def get_memory_usage(): + mem = psutil.virtual_memory() + return mem.percent, mem.used / (1024 ** 3), mem.total / (1024 ** 3) + +def get_cpu_usage(): + return psutil.cpu_percent(interval=1) # 获取CPU利用率(百分比) + +def get_gpu_usage(): + try: + nvmlInit() + device_count = nvmlDeviceGetCount() + gpu_stats = [] + for i in range(device_count): + handle = nvmlDeviceGetHandleByIndex(i) + name = nvmlDeviceGetName(handle).decode("utf-8") + mem_info = nvmlDeviceGetMemoryInfo(handle) + util = nvmlDeviceGetUtilizationRates(handle) + gpu_stats.append({ + "name": name, + "gpu_util": util.gpu, + "mem_util": util.memory, + "mem_used": mem_info.used / (1024 ** 3), + "mem_total": mem_info.total / (1024 ** 3) + }) + nvmlShutdown() + return gpu_stats + except Exception: + return [{"name": "N/A", "gpu_util": 0, "mem_util": 0, "mem_used": 0, "mem_total": 0}] + +def main(): + log_file = "monitor_log.txt" + with open(log_file, "a", encoding="utf-8") as f: + while True: + now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + # CPU + cpu_percent = get_cpu_usage() + + # 内存 + mem_percent, mem_used, mem_total = get_memory_usage() + + # GPU + gpu_stats = get_gpu_usage() + + # 拼接日志内容 + log_line = (f"[{now}] CPU: {cpu_percent:.1f}% | " + f"Memory: {mem_percent:.1f}% ({mem_used:.2f} GB / {mem_total:.2f} GB)") + + for gpu in gpu_stats: + log_line += (f" | GPU: {gpu['name']} {gpu['gpu_util']}% " + f"(Mem {gpu['mem_used']:.2f} GB / {gpu['mem_total']:.2f} GB)") + + # 输出 + print(log_line) + f.write(log_line + "\n") + f.flush() + + time.sleep(9) # 这里+CPU统计的1秒,总体大约10秒一个周期 + +if __name__ == "__main__": + main() diff --git a/backend/tests/testfemtobolt.py b/backend/tests/testfemtobolt.py new file mode 100644 index 00000000..1645f585 --- /dev/null +++ b/backend/tests/testfemtobolt.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import os +import time +import numpy as np +import matplotlib.pyplot as plt +from matplotlib.colors import LinearSegmentedColormap + +class FemtoBoltViewer: + def __init__(self, depth_min=900, depth_max=1300): + self.depth_min = depth_min + self.depth_max = depth_max + + # 自定义colormap + colors = ['fuchsia', 'red', 'yellow', 'lime', 'cyan', 'blue', + 'fuchsia', 'red', 'yellow', 'lime', 'cyan', 'blue'] + self.cmap = LinearSegmentedColormap.from_list("custom_cmap", colors) + + # SDK设备句柄 + self.device_handle = None + self.pykinect = None + self.config = None + + # 初始化matplotlib figure + plt.ion() + self.fig, self.ax = plt.subplots(figsize=(7, 7)) + + def _load_sdk(self): + """加载FemtoBolt SDK""" + try: + import pykinect_azure as pykinect + self.pykinect = pykinect + # 配置DLL路径 + base_dir = os.path.dirname(os.path.abspath(__file__)) + dll_path = os.path.join(base_dir, "..", "dll", "femtobolt", "bin", "k4a.dll") + self.pykinect.initialize_libraries(track_body=False, module_k4a_path=dll_path) + return True + except Exception as e: + print(f"加载SDK失败: {e}") + return False + + def _configure_device(self): + """配置FemtoBolt设备""" + self.config = self.pykinect.default_configuration + self.config.depth_mode = self.pykinect.K4A_DEPTH_MODE_NFOV_UNBINNED + self.config.camera_fps = self.pykinect.K4A_FRAMES_PER_SECOND_15 + self.config.synchronized_images_only = False + self.config.color_resolution = 0 + self.device_handle = self.pykinect.start_device(config=self.config) + + def _render_depth(self, depth_image: np.ndarray): + """使用matplotlib绘制深度图,带背景和自定义colormap""" + # 过滤深度范围 + depth = np.where((depth_image >= self.depth_min) & (depth_image <= self.depth_max), + depth_image, 0) + + # 屏蔽深度为0的部分 + depth = np.ma.masked_equal(depth, 0) + + # 背景图(灰色) + background = np.ones_like(depth) * 0.5 + + self.ax.clear() + # 绘制背景 + self.ax.imshow(background, origin='lower', cmap='gray', alpha=0.3) + # 绘制白色栅格线 + self.ax.grid(True, which='both', axis='both', color='white', linestyle='-', linewidth=1, zorder=0) + # 绘制深度等高线 + self.ax.contourf(depth, levels=200, cmap=self.cmap, vmin=self.depth_min, vmax=self.depth_max, origin='upper', zorder=2) + plt.pause(0.001) + plt.draw() + + def run(self): + if not self._load_sdk(): + print("SDK加载失败,退出") + return + + self._configure_device() + print("FemtoBolt深度相机启动成功,按 Ctrl+C 退出") + + try: + while True: + capture = self.device_handle.update() + if capture is None: + continue + + ret, depth_image = capture.get_depth_image() + if not ret or depth_image is None: + continue + + self._render_depth(depth_image) + + except KeyboardInterrupt: + print("退出程序") + finally: + self.device_handle.stop() + self.device_handle.close() + plt.close(self.fig) + + +if __name__ == "__main__": + viewer = FemtoBoltViewer(depth_min=900, depth_max=1300) + viewer.run() diff --git a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt b/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt deleted file mode 100644 index 75ffb360..00000000 --- a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt +++ /dev/null @@ -1,20 +0,0 @@ -[08/15 15:17:24.789452][debug][11108][Context.cpp:30] Context creating, work_dir=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend -[08/15 15:17:24.789708][debug][11108][Context.cpp:49] Config file version=1.1 -[08/15 15:17:24.789777][debug][11108][FrameBufferManager.cpp:23] Max global frame buffer size updated! size=2048.000MB -[08/15 15:17:24.789813][info][11108][Context.cpp:68] Context created with config: default config! -[08/15 15:17:24.789902][info][11108][Context.cpp:73] Work directory=D:\Trae_space\BodyBalanceEvaluation\frontend\src\renderer\dist-electron\win-unpacked\resources\backend, SDK version=v1.10.11-20240724-aeaa107e5 -[08/15 15:17:24.790186][debug][11108][DeviceManager.cpp:30] DeviceManager init ... -[08/15 15:17:24.790207][info][11108][MfPal.cpp:105] createObPal: create WinPal! -[08/15 15:17:24.790229][debug][11108][MfPal.cpp:110] WmfPal init ... -[08/15 15:17:24.816565][debug][11108][MfPal.cpp:117] WmfPal created! -[08/15 15:17:24.816602][debug][11108][DeviceManager.cpp:34] Enable USB Device Enumerator ... -[08/15 15:17:24.842574][debug][11108][EnumeratorLibusb.cpp:321] queryDevicesInfo done! -[08/15 15:17:24.842902][debug][11108][MfPal.cpp:216] Create WinEventDeviceWatcher! -[08/15 15:17:24.843143][debug][11108][UsbDeviceEnumerator.cpp:78] No matched usb device found! -[08/15 15:17:24.843178][info][11108][DeviceManager.cpp:15] Current found device(s): (0) -[08/15 15:17:24.843199][debug][11108][DeviceManager.cpp:52] DeviceManager construct done! -[08/15 15:17:24.843226][debug][11108][Context.cpp:81] Context destroying ... -[08/15 15:17:24.843236][debug][11108][DeviceManager.cpp:56] DeviceManager destroy ... -[08/15 15:17:24.843245][debug][11108][DeviceManager.cpp:64] DeviceManager Destructors done -[08/15 15:17:24.983361][debug][11108][MfPal.cpp:128] WmfPal destroyed! -[08/15 15:17:24.984152][info][11108][Context.cpp:84] Context destroyed diff --git a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/dll/smitsense/SMiTSenseUsbWrapper.dll b/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/dll/smitsense/SMiTSenseUsbWrapper.dll deleted file mode 100644 index 04eb4e48a87e2a8a555e52e8525772641def850b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18944 zcmeHv4Rl-8mF|%(*@_cI3bDWe(kMYDhG56BQgCogBh^oFB-&OZC>v(G+z@AGr7sk?I@%VvxjkyMqj9zc5B;`cxQF&h}0 zIq%?1_QbTK^Lq^5qw^cK1fq^`B(yow)b40*3I;=xW0T(zi3J^jprhvYHIDXBi@(HT z$#d$gAH3tbubnDgc{-i`z>l9kiSpNKZ$5QYz*VQ77qIHoYXVlEdKqxwuIf{-0@`b< z1^!fN_33{@d3Nosr%nsFQiuO?S}VInd3B(93-vX1Wc9U-wcMY>4s3mBOS*29EpSZF znmLy_fVob??k@ziiF_x9ujR8C%Mqvv$o2t2O2w1S8bPWxN`ni_8m83E*eKdKsubQAdry>HG-IIvHyKXmAMk5;o|w*)+-#kdrP}|Ac*uQd@3^Z^YpDNn`3^Hrn*3!1zp`L9!eqJLU2*Wj(~>@I9<=7}1o+m*7eKm5M+KCiQg%H`GrmTM6$tp`l=2>fh- zHy`ghEalOtij)yEa8H_YQMEf|-TeqDDOvpe8er$)hszCMBU|1iL$qrJm$|bEEb8OO$9Toywi!xw9yOTzaW$bo z^=W*1pM~{y3TkLk2xPi&b#%`<(}l< zm_6B_R9g=iOIj1P$9N+4BGOw(f9CP~3T#~V*&^_g6+HftEjr6;%jNyGf3~8zbyn?* zBY(ri2+-MmTz;R&e`nKK%17{@kt|oro2)iKe=m~hqxEdWFA~soh9~ZR3vuMf zRg7T>H6=wnS$7QMI9qLfvZmzPJ+wKm;}yrF7P#MfSY7}mBURg0prcl5w5VTq-s#?CZNIWI8`TTo zc;ImvF-R`I#B~CWtQ@q1I{P)An0qza^*~nk6tu+dH$9B9au{0+*mv1Ss_K;oyh+mr zAY3PS0+G4S$`f#G*I@)VhiZR;fYRsV9O1G(iyF$uyv==e*(v3Zq_d=@SdbqeR02_nxT2<9+2kSv|xn`eQ%UhL4rRt<9Zk zA03wl<6paumK2XX%H_8`w3G|E= zwJtRs2p5kB!hfYo3~-JSP(;Dx21LO>4nfUhBXd-H5siZ;El>Fn!GOyrN3uQgYh3=r zEy)>C&!QJvYnI&JuD8*-l7f)E6dtH^L@Nrdh`8}`vu2A?ejTri^Cv{_s1O5RsUvv#N5jM}FnWS|Y z`;1RC1=9>@+C4z+gmynA?S86jcfUZ-SaG3h@2485_BXWFT%g(l)qWJ=Kv(TQi1p|s z)|gkk@-sX+W2<-3Z(S!SXxxEqjz_)}%BquBnMqUKP%<<{g!1n_^6zLHgHd2eq$;7H zs(bDkz8?K6Accmee>h1)WQ@@BA1)=&zBwNhlOIwKww`Uy0`eP&=1KBP9{F8(nnzZ+ ze4Z@A^}p}N@WBD)i?sDg8bdr$K_a__Hz$qTX$QuITVo6ebQJtl`_Cria?@-&_-_!j z>A8Q_F!*VF|bX{+H_BH`7~Z} zo?BO*cfC@Fd0D}e`509y9R=)lvRjWssq~g|yO##=IGltw@`<@OqA%>eO$(^^ichyk zc{ZF&6}t?m_IB(tj%~X~=_460nEK09`-$0N>Q!kv9e*AYLpiDKi0A&UoJIu6(a*CY zPXkJro}oOHb5EaAf%WT%^7;4mSsjnB>MYfMJN)P%t;Dbxt@tpFUMJyS!DgRsKX@qZ)WOw1P_>n5* zC36mn_yHePv{oFE20{p2A8pqc9al^=2 zNGaOhpta3az5=ErbCh`~V=gvmLKw?ZBe}{QC_IkCA(~Pf*I}g+kpahuZ)^P0ynLYB zor+PTOO(}GAy@GrGxAPGd*udTlrsG2NNNwW!rGI0CUbb?bBO6U*2=@mKCDqVHC-D2 z$Pk+a*TgC-AH{JN`$$X(onJQK{2}64DOrCxS-qRI<{Q3$2F)z=bgN(@+2EX`P!kPk z_yJTZ|BN4SHz{3$bYh%Iy*#`d8O;uG(EBkOcOCu}bik}NeS1v*h_bR0bE(`7GF=1t zO1)N?27T2cJM#A4iPL`iOw&lyg(J=8_&LM&dzBwy5-^iRU9ZHPkn$E{{1VPn5RRl? zP36O)ER{gZu2yTv^b-Uj9v@K2Vdj7Uw!8p9_>Wdx1n`XM!^MmlgvMb>Pa#+^}Pi^qx-^`uV*@fY3s78f>;BhrS(8n zK2P3JfRhrkIS8$t-sFsDId;sRisMp&e9H`PMQfyWbU%()zK8n=oPJ-5N#U1h77t5;%`MNN||;M+mFdZ{s|U zVgZa$_Z^8A6)aFln!+pRIuRLB1q_yj}`0Y3K zqCh6C6rkJPDd~3QhZvi(ntpcR2M4fA&^r`Gn>3|W^T=zIUxFvd9jJeu?g5gPUjQg> z)c2d(z?8PW?lKhaxp1~ZUK@-cbg-GQXR$TH`GVGfci|+6(4bBqXW8`Qq`%S;p zb3Jee#qvoa zgd^3S3wPx5FUG*srPjg+k_~i2_dC^Yry7iP8IQL(%OQc6ZtYNun*~x!DW;E*Vf|Wx zrpKY$KZj;&e_YMl`E!~)-#y|Mo5!1MU}5TnEc!*UUt}RHT@*teF3!iw35tnH`_G_n z4bh946>|?a(XVA~u9WMrwYx^{t#!RpBR^NQYcweN+p2cGd$T_ntC3I0qprhMxHYPs zK49&x+Vx%_==UUy&f4kELP(>0j%GE{;G7Yw=W)wbMUc2~bcJfIOywAuR8-lRlf|U@ zm0<#xn;t*+G5pT4-N~OaV18(Zxb=Iz(fk_Ow50@5C_Z4QNto_Nb|e?L24s3D;?yMh zM-`(xuO?a1dyPz%ZA|xVJ^06I(Y)H}y=XfhP5MoLyHK0|nuO)6$XXA+m2ZuIofsu7 z+kyC%xK5cH*6OZ^?f}J=(J0s(26^x64nBR5V^j{ZM8|@@8~bAGWz$Mf#|O;)rX|Q= zU&<1u#mH5i>2pZs@c~D_sQ^V<_7kQ{k?R4~V9ix0Ybs903Ir$T(DI>}C2l!;HR5$* z?!N<&hZRx4l;CLbr)}ceL%x>3qFC@5?ath)xN5|&-h6J68|2_leH zJr7c^!mqpbket_&ShCgvfwvLixE|+AH(&!9ifVrq14Y!PhyZ?AVR^E4)cP%Gq{hjq z)6*cJ-rTDFWn>Y5XJ9*HPV#K!HzMXfvi^LdSYcbgbp_J8&Qe zq6{wt<*&-RUn~B51Q%Y>7n6A0Uw{tkaP%!BK5{M2m$kU8zY-;!zm`zmtmTt+4y{c0 z<#Me9&2Tyya&Q;&u38&+_421MHF&`3DimB3U14j~i>UTnCv~zBi|<*Oqv@wqL`1z? zoDnEk;~uGUaW0e-%fb5x=z3y@L+{)QNpQ9BpHP9gkhIuQ9v&dQCrxvZ$Mort_U>;% z6dpG_l{d8v?mB4y1)A>&T6kRgo!H#jVw+Mq17PG5E@zXiKm;Srdc4ofD_)BXYikGO z#m!)5+qY(t0F^@knQaG1qZet5sX3w+p2rx}!l%XWlj8SL@%sQ!f$(JLR9W>?8>b6s=2@$5-&_Ho`k;Jxa&r@8MIIp40Om6_ zhHR@e9e3-7?D){AH+#VALN|x+8eZ})ojJvx220)+8&8B`WF7jR(<6TY0=-v97jeT7 z3(E`6aJbbsDs7lGxz^}xJ#$i6LG2Z(s8--6dyN0rBfH;AQu z{2-UH$x{oX2pwu+48Np){h2s#t3zpACd{ZWJU^)^Pwp*({)V4;n^vT~4$7NHuv3M< zz0GBV>+r5}OhC%|tzNkoehIvA!ngO z_{&fuA{0e*Z_@HAT6*Q$u)G$la9yMF`l!aK6g_JX%{^}RiXk)(QP$mGfxx(+DMVRm z#2ApDMRnt^l(0{*0YSGnQ(te<#mv?d<>||rD=%_6vn?07M`(H%{{>7TKN z&_m};%4_~e5^C~U{Dj0k)*8qfpgdkHj=y5mFQu;(?-uDDW5)YM^f!z8Q@)EE4y`kL zMrV|t=uGb~UMWZhdoXb)n`9BQK$(pH(VbvFyZYPbgP)L~eMdv$oPPWJ=7 ze5nr4>-aZyctVFy=`&^y;uohtKNccz()wuGH((e9Km8 zxf%NSuhz@y`a^nI(cy9(-loI%boc`uzO2LaH!mOP{k^WkPxbn}I^2?`*E@XlTGmNx zbQ%s7Yh_k41*QeGX?U9s9Xc%4VVdm!6e1K(dOAO^_1CaKmq%w@dWTjX)Zv&;m(u0f zufxIXHM*(zO-(J1rZ#*M)FcH$LB@)1xH#R~;MQPhd(h$UX!eISQXzq8ABi;Wa0LC^ z9c}*LW@!smZ1Xoup@>5Yg&b|6;AUM~HvSzMe3!K&ef0(;EY9?#zcIu4kKyK-eT=$~ zr_|9RHiX7D+{D^&ilQgo4$JXpKIJsI{{@e&e)4>zyTDzluqRe_@p^G`NE82GfRaA1 zQ@a||qT;%zzI^vmrGUcFtGM{qLrDe&av!UP2=d=2! zwvT4;O!K0)#GCjaCVb|TjT*;A>d9~28Td@Td%$-Lb%-GsJXA+*>B$@q(WjpR%5>u2 zlxOvtnl*!$UA<&Od~fa7<=eZiaPNDc`mMfk%T4L7@x_Apbd&m-gLHFs zgO}OF=cY`23+ikM5N#uH^nH_tYexiyUUABG1Ez7#(o$wC!M=PP*MSc^@wq3MYcxAr zP$j+>rEgG60`$$NQ&=6nlPRKhrKQcS2%`mMjG3uiy&A7Gs;j7(131S7ZY|;5lj(44 zkIl^_oJX{+pNtc98z$lE1zqE0oS@q>3D+R#!oWc#x_k|St`j)AZAgC`OMd);AzgMx zNH)YLM)R-HIJbq`q;^sYtq(2ZZJl5G(wAB`(P}b!qppXD)a>+BiN41s;qDZDGrY(a z`Xagpjm|s?r_tGfD>b734()qeH$LVVC3!^I*?}x8b?Z34{!K1={VLID^F^a%j{!$( zmyW|%#)wP6Wz6eR!qGmf#57K)c|kpV6LsltgUL@j(`BfMVwkW6VIGkvs@M>459XlF zrl=^hM%V(4L;r4=zLN$Ddd2s_&Qe$t%D{CR#&JS!!vANF0oPwSWm%<$JR{3nn#1zS za>53qff>JoI7M(J{NyS#%Ws*<@*6BHf9VXC{|#H%4PR)0Zc)vO?ndTbBxBwn=DNPD z6rSZa@6Q@FPRq(FVsqx_u-OJX%N;ePvcp;RPh)-GgLd=HSLckMzY~MkogSsQ*eg4*=#Ow#!b*qUY5|;e2bA;TFj!o8$-VZ$t&7J4U@{4K+qA- zOuQjCo8_*|Zp^}*&tGC@OFl3%`y11lMalzDGaCdC!{v;9OXoSc41SYOmO#R%9uC*nQQdcAEv^6QYodfA8ky&3gX z-kMQAjqH7?m0cdWbSOVOyD>eshe7i|Azmnh4?!Wr>a2Q$HHTR-1}ny3Et?)TOf#@) zWQ%4aGe?YJSW&4)gZiZ=X1xS7b`!>Ykl77AEQj<%_PTT$vy|o3lOE|IDD+nfI46(I zfv)DjR_8#6bI{kEGW!tG(PP6)XL=eDOQ$}h&lK>=l)7x-9|Z1Vb!i^7m!K!TkDzs< zQ}0Vu;KznU@splRy3`aps!#99GU*zL!IURGo{lLpr^(Xo_qsJbA-pq68&GaT>O`Wr z(TT7_vE^|<`u49I_}?H6BE5~2UB&RgMp-s9vWskFN?O|5CgFWI`z3Fapc@3v44gZIE+2OSoxnLVaMuIZ2pp}Y>F3uR zIrMR{XC3J2`Ro0SricIE?{BhLYq&k?i$yp2Hv1)Ci?Cr|TOcaYpG=L{Mf^=IzEPM{DTa4L8Xi|S zTiI?cPg0K)+-CAs2U~nCA>XD*sHvs7DJuDzB%icJ=)m8B0kS7dBzlW4(8Aa+_58h| zK#<=5iC$wt!M|?Jy6XBROSJ*RpnSgSjt&uOcvG;Y%^z9H*aBcNZr}I~QCei|kEYf% zhr&B(Uk*OLXxQHzXbm*`wrH#<@)3KX)7pvarkhy1zdh=Q)P*`fX-C+P;kJg-J?Znk zu6p&_+ZsG|wUF4iL`)LP#A%p(=G)}o90+O(@pmw`nMwXgdmxBF%9b&3-qzF>Xz_)c z$X-z83qm#MEf|XUrC0=7!U&pidxop8EjT5%i;FbIRxn@Gzs1)YXro3Hs|!FE3bqC| z$0B}Tun9J@-PaV^ybZ4tsh@xpKu=#9ukr`C1tOtfyFZB0E}cXX3ewE^q^3=6h{Ma7 zFM=^dsUN+jH5P0Z_j8yJe}}(0Ci&A+tshbWyI~^l^F#X9 zh#$Q*g#*Ry(c;|;1=7l=r)rna>~bPFr(HLVS5hSD-_8>@t(v1XXo z_@&RRc4T3Ztd2z@&?+tou);;zcTElH8Be3wSF=4)y%375@;8yJ^$~xRCZh{=HU3Sp z&71v^G;X!=j+j5Pquw8B4Mp0Ug3TBiG~yKSlBoM5|HeD!minYnQU)> zQmdjEl%H~}&`=YI(sXOY-oU0vQ)EXqY%R({*|fN8#UlX29iop5PsNz5#Hj zo+tQqJ^wAh50KsfPl8T|#)n|Jo+r2qiTc_NXq~U+ZGev={Tgiv9?|gxKh*PMfMp9b z`lW!kB9Tt;evsX#<2wNdbv(g0ufqL2cq)KxPK@U!4SuH(<3^tT9}cIHQpgVh=3lMx zC-^4PAn*#{7Z&2Yhx|^!hmgqr307W%@dCdQ@Q3(7@dMv{C{*XO^D1`Oie z1j1tH z@wYcci`xUukx(?$Dit?}+HY)%wwG*Mve1D|X`mIy$90*#ftACtT#Ce^k_U$xo!f%H zz|FNlv_s1%juw%Cv_mfg6Y+mBhF<(F^^w3f>|QtfqvMsC)wSYOf?<38+x%_#W&ppH z3!9>z;I`0Ke`KK}7N}~b?Rn+G)~2?of1zXXIG^Qxpx(B;Yb`N!rb+hl8 uzgP5#`w{-g;3LW-qmPU|!oJVy5tqQDe1GHqJ^Oq1U)afCG5@b>;6DIB_ns&K diff --git a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/start_backend.bat b/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/start_backend.bat deleted file mode 100644 index 917f45ab..00000000 --- a/frontend/src/renderer/dist-electron/win-unpacked/resources/backend/start_backend.bat +++ /dev/null @@ -1,17 +0,0 @@ -@echo off -echo 启动身体平衡评估系统后端服务(完整版)... -echo. -echo 服务信息: -echo - HTTP API: http://localhost:5000 -echo - SocketIO: ws://localhost:5000 -echo - 管理界面: http://localhost:5000 -echo. -echo 按Ctrl+C停止服务 -echo. -cd BodyBalanceBackend -"BodyBalanceBackend.exe" -if %errorlevel% neq 0 ( - echo. - echo 服务启动失败,请检查错误信息 - pause -) diff --git a/frontend/src/renderer/src/views/Detection.vue b/frontend/src/renderer/src/views/Detection.vue index b72765c0..1b18de22 100644 --- a/frontend/src/renderer/src/views/Detection.vue +++ b/frontend/src/renderer/src/views/Detection.vue @@ -834,7 +834,7 @@ function connectWebSocket() { // 创建主Socket.IO连接 socket = io(BACKEND_URL, { - transports: ['polling'], // 只使用polling,与后端保持一致 + transports: ['websocket', 'polling'], // 只使用polling,与后端保持一致 timeout: 10000, forceNew: true, reconnection: true, @@ -844,7 +844,7 @@ function connectWebSocket() { // 创建统一设备命名空间连接 devicesSocket = io(BACKEND_URL + '/devices', { - transports: ['polling'], // 只使用polling,与后端保持一致 + transports: ['websocket', 'polling'], // 只使用polling,与后端保持一致 timeout: 10000, forceNew: true }) @@ -2722,7 +2722,11 @@ onUnmounted(() => { } :deep(.el-form-item__label) { - color: #ffffff; + color: #ffffff !important; + font-size: 14px !important; + font-family: '苹方 粗体', '苹方 中等', '苹方', sans-serif !important; + font-weight: 700 !important; + font-style: normal !important; } diff --git a/frontend/src/renderer/src/views/PatientCreate.vue b/frontend/src/renderer/src/views/PatientCreate.vue index 2c5b0ca0..94422d62 100644 --- a/frontend/src/renderer/src/views/PatientCreate.vue +++ b/frontend/src/renderer/src/views/PatientCreate.vue @@ -433,8 +433,11 @@ const handleSaveAndDetect = async () => { } :deep(.el-form-item__label) { - font-weight: 500; - color: #606266; + font-size: 14px !important; + font-family: '苹方 粗体', '苹方 中等', '苹方', sans-serif !important; + font-weight: 700 !important; + font-style: normal !important; + color: #FFFFFF !important; } :deep(.el-form-item__content) { From 365d0b54fd87bda7456692b1fde9cf92f7a484ac Mon Sep 17 00:00:00 2001 From: root <13910913995@163.com> Date: Tue, 19 Aug 2025 14:03:34 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=BA=86cofing=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=89=93=E5=8C=85=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/build_app.py | 3 +- backend/devices/utils/config.ini | 2 +- backend/devices/utils/config_manager.py | 29 ++++++++++--- backend/dll/smitsense/SMiTSenseUsbWrapper.dll | Bin 18432 -> 0 bytes backend/main.py | 6 +-- .../resources/backend/Log/OrbbecSDK.log.txt | 40 ------------------ 6 files changed, 30 insertions(+), 50 deletions(-) delete mode 100644 backend/dll/smitsense/SMiTSenseUsbWrapper.dll delete mode 100644 frontend/src/renderer/dist-electron/win-unpacked/resources/backend/Log/OrbbecSDK.log.txt diff --git a/backend/build_app.py b/backend/build_app.py index 4bda99e1..d6864832 100644 --- a/backend/build_app.py +++ b/backend/build_app.py @@ -42,7 +42,8 @@ a = Analysis( ('dll/femtobolt/bin/ob_usb.dll', 'dll/femtobolt/bin'), # Orbbec USB库 ('dll/femtobolt/bin/live555.dll', 'dll/femtobolt/bin'), # Live555库 ('dll/femtobolt/bin/OrbbecSDKConfig_v1.0.xml', 'dll/femtobolt/bin'), # Orbbec配置文件 ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSense传感器库 - ('dll/smitsense/SMiTSenseUsbWrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 + ('dll/smitsense/SMiTSenseUsb-F3.0.dll', 'dll/smitsense'), # SMiTSenseUsb库 + ('dll/smitsense/Wrapper.dll', 'dll/smitsense'), # SMiTSense传感器库包装类 ], hiddenimports=[ 'flask', diff --git a/backend/devices/utils/config.ini b/backend/devices/utils/config.ini index 29a6fdba..4e1df162 100644 --- a/backend/devices/utils/config.ini +++ b/backend/devices/utils/config.ini @@ -29,7 +29,7 @@ depth_range_max = 1700 [DEVICES] imu_device_type = real -imu_port = COM3 +imu_port = COM7 imu_baudrate = 9600 pressure_device_type = real pressure_use_mock = False diff --git a/backend/devices/utils/config_manager.py b/backend/devices/utils/config_manager.py index 28f8a211..ec4dd2e9 100644 --- a/backend/devices/utils/config_manager.py +++ b/backend/devices/utils/config_manager.py @@ -43,19 +43,38 @@ class ConfigManager: Returns: str: 配置文件路径 """ + import sys + # 可能的配置文件路径 - possible_paths = [ - os.path.join(os.path.dirname(__file__), 'config.ini') - ] + possible_paths = [] + + # 如果是打包后的exe文件,从exe文件同级目录获取 + if getattr(sys, 'frozen', False): + # 打包后的exe文件路径 + exe_dir = os.path.dirname(sys.executable) + possible_paths.append(os.path.join(exe_dir, 'config.ini')) + + # 开发环境下的路径 + possible_paths.extend([ + os.path.join(os.path.dirname(__file__), 'config.ini'), + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini'), # backend/config.ini + os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))), 'config.ini') # 项目根目录/config.ini + ]) for path in possible_paths: abs_path = os.path.abspath(path) if os.path.exists(abs_path): self.logger.info(f"找到配置文件: {abs_path}") return abs_path - - + # 如果都找不到,返回默认路径(exe同级目录或backend目录) + if getattr(sys, 'frozen', False): + default_path = os.path.join(os.path.dirname(sys.executable), 'config.ini') + else: + default_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'config.ini') + + self.logger.warning(f"未找到配置文件,使用默认路径: {default_path}") + return default_path def _load_config(self): """ 加载配置文件 diff --git a/backend/dll/smitsense/SMiTSenseUsbWrapper.dll b/backend/dll/smitsense/SMiTSenseUsbWrapper.dll deleted file mode 100644 index cfa259cc4b855c1c348ef44b69948541f58db021..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18432 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 c