11 Commits

Author SHA1 Message Date
lennlouisgeek
de0d397dcd update raw_to_g1 table, fit 0528-5 2026-06-09 22:07:30 +08:00
lenn
da424d1637 Tune raw_to_g1 calibration 2026-06-09 18:41:24 +08:00
lenn
2c6fee9aad Update raw_to_g1 calibration points 2026-06-09 18:35:31 +08:00
lenn
2828eaafb5 Refine sample delivery force UI and mapping 2026-06-09 18:26:14 +08:00
lenn
821800beb1 refactor: add debug feature flag and cross-platform serial port filtering 2026-06-09 14:27:30 +08:00
lenn
8533747670 Add sample delivery changelog 2026-06-09 11:17:24 +08:00
lenn
160ff54368 Prepare sample delivery UI 2026-06-09 11:05:18 +08:00
lenn
79faa67dd8 update serial and spatial force components 2026-06-02 16:24:15 +08:00
lenn
78c4445b93 update devkit server and model files 2026-06-02 09:43:05 +08:00
lenn
0812142359 Fix HUD window controls 2026-05-26 14:33:51 +08:00
Ubuntu
39840f469f feat: customer demo - remove breakout game, numeric matrix, 3D module switcher
- Remove NeonBreakoutArena split panel from CenterStage
- Remove numeric matrix display mode toggle (keep dot matrix only)
- Remove 3D module stage mode switcher from HudPanel
- Remove precision-test config link and related UI state
- Cargo check passes (1 warning: unused import in log.rs)
2026-05-25 15:17:48 +08:00
64 changed files with 40599 additions and 7357 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
('d:\\JE-Skin-main\\devkit\\dist\\je-skin-devkit-server.exe', ('D:\\Workspace\\je-skin-customer-demo\\devkit\\dist\\je-skin-devkit-server.exe',
True, True,
False, False,
False, False,
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-console.ico', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-console.ico',
None, None,
False, False,
False, False,
@@ -29,142 +29,173 @@
None, None,
None, None,
None, None,
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\je-skin-devkit-server.pkg', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\je-skin-devkit-server.pkg',
[('pyi-contents-directory _internal', '', 'OPTION'), [('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', ('PYZ-00.pyz',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\PYZ-00.pyz', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\PYZ-00.pyz',
'PYZ'), 'PYZ'),
('struct', ('struct',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\struct.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\struct.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod01_archive', ('pyimod01_archive',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod01_archive.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod02_importers', ('pyimod02_importers',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod02_importers.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod03_ctypes', ('pyimod03_ctypes',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod03_ctypes.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod04_pywin32', ('pyimod04_pywin32',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod04_pywin32.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'), 'PYMODULE'),
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_inspect', ('pyi_rth_inspect',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil', ('pyi_rth_pkgutil',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_multiprocessing', ('pyi_rth_multiprocessing',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'), 'PYSOURCE'),
('sensor_server', 'D:\\JE-Skin-main\\devkit\\sensor_server.py', 'PYSOURCE'), ('sensor_server',
('python314.dll', 'C:\\Python314\\python314.dll', 'BINARY'), 'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_server.py',
('numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll', 'PYSOURCE'),
'C:\\Python314\\Lib\\site-packages\\numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll', ('python313.dll',
'C:\\Users\\Administrator\\miniconda3\\python313.dll',
'BINARY'), 'BINARY'),
('numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll', ('numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll',
'C:\\Python314\\Lib\\site-packages\\numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll',
'BINARY'), 'BINARY'),
('select.pyd', 'C:\\Python314\\DLLs\\select.pyd', 'EXTENSION'), ('numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll',
'BINARY'),
('select.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\select.pyd',
'EXTENSION'),
('_multiprocessing.pyd', ('_multiprocessing.pyd',
'C:\\Python314\\DLLs\\_multiprocessing.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_multiprocessing.pyd',
'EXTENSION'), 'EXTENSION'),
('_zstd.pyd', 'C:\\Python314\\DLLs\\_zstd.pyd', 'EXTENSION'), ('pyexpat.pyd',
('pyexpat.pyd', 'C:\\Python314\\DLLs\\pyexpat.pyd', 'EXTENSION'), 'C:\\Users\\Administrator\\miniconda3\\DLLs\\pyexpat.pyd',
('_lzma.pyd', 'C:\\Python314\\DLLs\\_lzma.pyd', 'EXTENSION'),
('_bz2.pyd', 'C:\\Python314\\DLLs\\_bz2.pyd', 'EXTENSION'),
('_ssl.pyd', 'C:\\Python314\\DLLs\\_ssl.pyd', 'EXTENSION'),
('_hashlib.pyd', 'C:\\Python314\\DLLs\\_hashlib.pyd', 'EXTENSION'),
('unicodedata.pyd', 'C:\\Python314\\DLLs\\unicodedata.pyd', 'EXTENSION'),
('_decimal.pyd', 'C:\\Python314\\DLLs\\_decimal.pyd', 'EXTENSION'),
('_socket.pyd', 'C:\\Python314\\DLLs\\_socket.pyd', 'EXTENSION'),
('_ctypes.pyd', 'C:\\Python314\\DLLs\\_ctypes.pyd', 'EXTENSION'),
('_queue.pyd', 'C:\\Python314\\DLLs\\_queue.pyd', 'EXTENSION'),
('numpy\\_core\\_multiarray_tests.cp314-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\_core\\_multiarray_tests.cp314-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\_core\\_multiarray_umath.cp314-win_amd64.pyd', ('_ssl.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\_core\\_multiarray_umath.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_ssl.pyd',
'EXTENSION'), 'EXTENSION'),
('_wmi.pyd', 'C:\\Python314\\DLLs\\_wmi.pyd', 'EXTENSION'), ('_hashlib.pyd',
('_overlapped.pyd', 'C:\\Python314\\DLLs\\_overlapped.pyd', 'EXTENSION'), 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_hashlib.pyd',
('_asyncio.pyd', 'C:\\Python314\\DLLs\\_asyncio.pyd', 'EXTENSION'),
('numpy\\linalg\\_umath_linalg.cp314-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\linalg\\_umath_linalg.cp314-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\mtrand.cp314-win_amd64.pyd', ('unicodedata.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\mtrand.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\unicodedata.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\bit_generator.cp314-win_amd64.pyd', ('_decimal.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\bit_generator.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_decimal.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_sfc64.cp314-win_amd64.pyd', ('_socket.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_sfc64.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_socket.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_philox.cp314-win_amd64.pyd', ('_lzma.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_philox.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_lzma.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_pcg64.cp314-win_amd64.pyd', ('_bz2.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_pcg64.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_bz2.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_mt19937.cp314-win_amd64.pyd', ('_ctypes.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_mt19937.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_ctypes.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_generator.cp314-win_amd64.pyd', ('_queue.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_generator.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_queue.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_common.cp314-win_amd64.pyd', ('numpy\\_core\\_multiarray_tests.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_common.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\_core\\_multiarray_tests.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_bounded_integers.cp314-win_amd64.pyd', ('numpy\\_core\\_multiarray_umath.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_bounded_integers.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\_core\\_multiarray_umath.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\fft\\_pocketfft_umath.cp314-win_amd64.pyd', ('_overlapped.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\fft\\_pocketfft_umath.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_overlapped.pyd',
'EXTENSION'), 'EXTENSION'),
('_elementtree.pyd', 'C:\\Python314\\DLLs\\_elementtree.pyd', 'EXTENSION'), ('_asyncio.pyd',
('grpc\\_cython\\cygrpc.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_asyncio.pyd',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\cygrpc.cp314-win_amd64.pyd', 'EXTENSION'),
('_wmi.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\_wmi.pyd',
'EXTENSION'),
('numpy\\linalg\\_umath_linalg.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\linalg\\_umath_linalg.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\mtrand.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\mtrand.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\bit_generator.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\bit_generator.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_sfc64.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_sfc64.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_philox.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_philox.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_pcg64.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_pcg64.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_mt19937.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_mt19937.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_generator.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_generator.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_common.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_common.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_bounded_integers.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_bounded_integers.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\fft\\_pocketfft_umath.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\fft\\_pocketfft_umath.cp313-win_amd64.pyd',
'EXTENSION'),
('_elementtree.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\_elementtree.pyd',
'EXTENSION'),
('grpc\\_cython\\cygrpc.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\cygrpc.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('google\\_upb\\_message.pyd', ('google\\_upb\\_message.pyd',
'C:\\Python314\\Lib\\site-packages\\google\\_upb\\_message.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\google\\_upb\\_message.pyd',
'EXTENSION'), 'EXTENSION'),
('grpc_tools\\_protoc_compiler.cp314-win_amd64.pyd', ('grpc_tools\\_protoc_compiler.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\grpc_tools\\_protoc_compiler.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc_tools\\_protoc_compiler.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('api-ms-win-crt-filesystem-l1-1-0.dll', ('api-ms-win-crt-process-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-filesystem-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-process-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-locale-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-time-l1-1-0.dll', ('api-ms-win-crt-time-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-time-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-time-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-environment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-runtime-l1-1-0.dll', ('api-ms-win-crt-runtime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-runtime-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-runtime-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll', ('api-ms-win-crt-locale-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-convert-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-heap-l1-1-0.dll', ('api-ms-win-crt-heap-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-heap-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-heap-l1-1-0.dll',
'BINARY'), 'BINARY'),
('zlib.dll', 'C:\\Users\\Administrator\\miniconda3\\zlib.dll', 'BINARY'),
('api-ms-win-crt-string-l1-1-0.dll', ('api-ms-win-crt-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-string-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-string-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-process-l1-1-0.dll', ('api-ms-win-crt-filesystem-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-process-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-filesystem-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-math-l1-1-0.dll', ('api-ms-win-crt-environment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-math-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-convert-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-stdio-l1-1-0.dll', ('api-ms-win-crt-stdio-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-stdio-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-stdio-l1-1-0.dll',
@@ -172,188 +203,221 @@
('api-ms-win-crt-conio-l1-1-0.dll', ('api-ms-win-crt-conio-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-conio-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-conio-l1-1-0.dll',
'BINARY'), 'BINARY'),
('VCRUNTIME140.dll', 'C:\\Python314\\VCRUNTIME140.dll', 'BINARY'), ('VCRUNTIME140.dll',
('VCRUNTIME140_1.dll', 'C:\\Python314\\VCRUNTIME140_1.dll', 'BINARY'), 'C:\\Users\\Administrator\\miniconda3\\VCRUNTIME140.dll',
('api-ms-win-crt-utility-l1-1-0.dll', 'BINARY'),
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-utility-l1-1-0.dll', ('api-ms-win-crt-math-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-math-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-private-l1-1-0.dll', ('api-ms-win-crt-private-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-private-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-private-l1-1-0.dll',
'BINARY'), 'BINARY'),
('libcrypto-3.dll', 'C:\\Python314\\DLLs\\libcrypto-3.dll', 'BINARY'), ('api-ms-win-crt-utility-l1-1-0.dll',
('libssl-3.dll', 'C:\\Python314\\DLLs\\libssl-3.dll', 'BINARY'), 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-utility-l1-1-0.dll',
('libffi-8.dll', 'C:\\Python314\\DLLs\\libffi-8.dll', 'BINARY'), 'BINARY'),
('python3.dll', 'C:\\Python314\\python3.dll', 'BINARY'), ('VCRUNTIME140_1.dll',
'C:\\Users\\Administrator\\miniconda3\\VCRUNTIME140_1.dll',
'BINARY'),
('libexpat.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libexpat.dll',
'BINARY'),
('libssl-3-x64.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libssl-3-x64.dll',
'BINARY'),
('libcrypto-3-x64.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libcrypto-3-x64.dll',
'BINARY'),
('libmpdec-4.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libmpdec-4.dll',
'BINARY'),
('liblzma.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\liblzma.dll',
'BINARY'),
('LIBBZ2.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\LIBBZ2.dll',
'BINARY'),
('ffi.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\ffi.dll',
'BINARY'),
('python3.dll',
'C:\\Users\\Administrator\\miniconda3\\python3.dll',
'BINARY'),
('ucrtbase.dll', ('ucrtbase.dll',
'C:\\Users\\Administrator\\miniconda3\\ucrtbase.dll', 'C:\\Users\\Administrator\\miniconda3\\ucrtbase.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-1.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'),
('api-ms-win-core-errorhandling-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-debug-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-timezone-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-console-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-2-0.dll', ('api-ms-win-core-synch-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-2-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-2-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll', ('api-ms-win-core-errorhandling-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-interlocked-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll', ('api-ms-win-core-console-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-handle-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll', ('api-ms-win-core-processthreads-l1-1-1.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-libraryloader-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-fibers-l1-1-0.dll', ('api-ms-win-core-debug-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-fibers-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll', ('api-ms-win-core-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-heap-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-file-l1-1-0.dll', ('api-ms-win-core-file-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll', ('api-ms-win-core-timezone-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-sysinfo-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-processenvironment-l1-1-0.dll', ('api-ms-win-core-processenvironment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processenvironment-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processenvironment-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-sysinfo-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-interlocked-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-libraryloader-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-handle-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-1-0.dll', ('api-ms-win-core-synch-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-1-0.dll',
'BINARY'), 'BINARY'),
('grpc\\_cython\\_credentials\\roots.pem', ('api-ms-win-core-fibers-l1-1-0.dll',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_credentials\\roots.pem', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-fibers-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('sensor_stream_pb2.py',
'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_stream_pb2.py',
'DATA'), 'DATA'),
('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h', ('sensor_stream_pb2_grpc.py',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_stream_pb2_grpc.py',
'DATA'), 'DATA'),
('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc', ('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING', ('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\INSTALLER', ('grpc\\_cython\\_credentials\\roots.pem',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\INSTALLER', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_credentials\\roots.pem',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\REQUESTED', ('numpy-2.4.6.dist-info\\WHEEL',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\REQUESTED', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\WHEEL',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md', ('numpy-2.4.6.dist-info\\REQUESTED',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\REQUESTED',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\RECORD', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\RECORD', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\WHEEL', ('numpy-2.4.6.dist-info\\INSTALLER',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\WHEEL', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\INSTALLER',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\ma\\LICENSE',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\ma\\LICENSE',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\ma\\LICENSE', ('numpy-2.4.6.dist-info\\entry_points.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\ma\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\entry_points.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\entry_points.txt', ('numpy-2.4.6.dist-info\\licenses\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\entry_points.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\METADATA', ('numpy-2.4.6.dist-info\\DELVEWHEEL',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\METADATA', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\DELVEWHEEL',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\DELVEWHEEL', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\DELVEWHEEL', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE', ('numpy-2.4.6.dist-info\\METADATA',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\METADATA',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md', ('numpy-2.4.6.dist-info\\RECORD',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\RECORD',
'DATA'),
('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE',
'DATA'),
('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\base_library.zip', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\base_library.zip',
'DATA')], 'DATA')],
[], [],
False, False,
False, False,
1779678963, 1780277624,
[('run.exe', [('run.exe',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\run.exe', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\run.exe',
'EXECUTABLE')], 'EXECUTABLE')],
'C:\\Python314\\python314.dll') 'C:\\Users\\Administrator\\miniconda3\\python313.dll')

View File

@@ -1,4 +1,4 @@
('d:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\je-skin-devkit-server.pkg', ('D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\je-skin-devkit-server.pkg',
{'BINARY': True, {'BINARY': True,
'DATA': True, 'DATA': True,
'EXECUTABLE': True, 'EXECUTABLE': True,
@@ -10,139 +10,170 @@
'SYMLINK': False}, 'SYMLINK': False},
[('pyi-contents-directory _internal', '', 'OPTION'), [('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz', ('PYZ-00.pyz',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\PYZ-00.pyz', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\PYZ-00.pyz',
'PYZ'), 'PYZ'),
('struct', ('struct',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\struct.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\struct.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod01_archive', ('pyimod01_archive',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod01_archive.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod02_importers', ('pyimod02_importers',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod02_importers.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod03_ctypes', ('pyimod03_ctypes',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod03_ctypes.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'), 'PYMODULE'),
('pyimod04_pywin32', ('pyimod04_pywin32',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod04_pywin32.pyc', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'), 'PYMODULE'),
('pyiboot01_bootstrap', ('pyiboot01_bootstrap',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_inspect', ('pyi_rth_inspect',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_pkgutil', ('pyi_rth_pkgutil',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'), 'PYSOURCE'),
('pyi_rth_multiprocessing', ('pyi_rth_multiprocessing',
'C:\\Python314\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'), 'PYSOURCE'),
('sensor_server', 'D:\\JE-Skin-main\\devkit\\sensor_server.py', 'PYSOURCE'), ('sensor_server',
('python314.dll', 'C:\\Python314\\python314.dll', 'BINARY'), 'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_server.py',
('numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll', 'PYSOURCE'),
'C:\\Python314\\Lib\\site-packages\\numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll', ('python313.dll',
'C:\\Users\\Administrator\\miniconda3\\python313.dll',
'BINARY'), 'BINARY'),
('numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll', ('numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll',
'C:\\Python314\\Lib\\site-packages\\numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy.libs\\libscipy_openblas64_-63c857e738469261263c764a36be9436.dll',
'BINARY'), 'BINARY'),
('select.pyd', 'C:\\Python314\\DLLs\\select.pyd', 'EXTENSION'), ('numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy.libs\\msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll',
'BINARY'),
('select.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\select.pyd',
'EXTENSION'),
('_multiprocessing.pyd', ('_multiprocessing.pyd',
'C:\\Python314\\DLLs\\_multiprocessing.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_multiprocessing.pyd',
'EXTENSION'), 'EXTENSION'),
('_zstd.pyd', 'C:\\Python314\\DLLs\\_zstd.pyd', 'EXTENSION'), ('pyexpat.pyd',
('pyexpat.pyd', 'C:\\Python314\\DLLs\\pyexpat.pyd', 'EXTENSION'), 'C:\\Users\\Administrator\\miniconda3\\DLLs\\pyexpat.pyd',
('_lzma.pyd', 'C:\\Python314\\DLLs\\_lzma.pyd', 'EXTENSION'),
('_bz2.pyd', 'C:\\Python314\\DLLs\\_bz2.pyd', 'EXTENSION'),
('_ssl.pyd', 'C:\\Python314\\DLLs\\_ssl.pyd', 'EXTENSION'),
('_hashlib.pyd', 'C:\\Python314\\DLLs\\_hashlib.pyd', 'EXTENSION'),
('unicodedata.pyd', 'C:\\Python314\\DLLs\\unicodedata.pyd', 'EXTENSION'),
('_decimal.pyd', 'C:\\Python314\\DLLs\\_decimal.pyd', 'EXTENSION'),
('_socket.pyd', 'C:\\Python314\\DLLs\\_socket.pyd', 'EXTENSION'),
('_ctypes.pyd', 'C:\\Python314\\DLLs\\_ctypes.pyd', 'EXTENSION'),
('_queue.pyd', 'C:\\Python314\\DLLs\\_queue.pyd', 'EXTENSION'),
('numpy\\_core\\_multiarray_tests.cp314-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\_core\\_multiarray_tests.cp314-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\_core\\_multiarray_umath.cp314-win_amd64.pyd', ('_ssl.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\_core\\_multiarray_umath.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_ssl.pyd',
'EXTENSION'), 'EXTENSION'),
('_wmi.pyd', 'C:\\Python314\\DLLs\\_wmi.pyd', 'EXTENSION'), ('_hashlib.pyd',
('_overlapped.pyd', 'C:\\Python314\\DLLs\\_overlapped.pyd', 'EXTENSION'), 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_hashlib.pyd',
('_asyncio.pyd', 'C:\\Python314\\DLLs\\_asyncio.pyd', 'EXTENSION'),
('numpy\\linalg\\_umath_linalg.cp314-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\linalg\\_umath_linalg.cp314-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\mtrand.cp314-win_amd64.pyd', ('unicodedata.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\mtrand.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\unicodedata.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\bit_generator.cp314-win_amd64.pyd', ('_decimal.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\bit_generator.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_decimal.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_sfc64.cp314-win_amd64.pyd', ('_socket.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_sfc64.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_socket.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_philox.cp314-win_amd64.pyd', ('_lzma.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_philox.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_lzma.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_pcg64.cp314-win_amd64.pyd', ('_bz2.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_pcg64.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_bz2.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_mt19937.cp314-win_amd64.pyd', ('_ctypes.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_mt19937.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_ctypes.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_generator.cp314-win_amd64.pyd', ('_queue.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_generator.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_queue.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_common.cp314-win_amd64.pyd', ('numpy\\_core\\_multiarray_tests.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_common.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\_core\\_multiarray_tests.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\random\\_bounded_integers.cp314-win_amd64.pyd', ('numpy\\_core\\_multiarray_umath.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\random\\_bounded_integers.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\_core\\_multiarray_umath.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('numpy\\fft\\_pocketfft_umath.cp314-win_amd64.pyd', ('_overlapped.pyd',
'C:\\Python314\\Lib\\site-packages\\numpy\\fft\\_pocketfft_umath.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_overlapped.pyd',
'EXTENSION'), 'EXTENSION'),
('_elementtree.pyd', 'C:\\Python314\\DLLs\\_elementtree.pyd', 'EXTENSION'), ('_asyncio.pyd',
('grpc\\_cython\\cygrpc.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\DLLs\\_asyncio.pyd',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\cygrpc.cp314-win_amd64.pyd', 'EXTENSION'),
('_wmi.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\_wmi.pyd',
'EXTENSION'),
('numpy\\linalg\\_umath_linalg.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\linalg\\_umath_linalg.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\mtrand.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\mtrand.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\bit_generator.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\bit_generator.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_sfc64.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_sfc64.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_philox.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_philox.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_pcg64.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_pcg64.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_mt19937.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_mt19937.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_generator.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_generator.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_common.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_common.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\random\\_bounded_integers.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\random\\_bounded_integers.cp313-win_amd64.pyd',
'EXTENSION'),
('numpy\\fft\\_pocketfft_umath.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy\\fft\\_pocketfft_umath.cp313-win_amd64.pyd',
'EXTENSION'),
('_elementtree.pyd',
'C:\\Users\\Administrator\\miniconda3\\DLLs\\_elementtree.pyd',
'EXTENSION'),
('grpc\\_cython\\cygrpc.cp313-win_amd64.pyd',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\cygrpc.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('google\\_upb\\_message.pyd', ('google\\_upb\\_message.pyd',
'C:\\Python314\\Lib\\site-packages\\google\\_upb\\_message.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\google\\_upb\\_message.pyd',
'EXTENSION'), 'EXTENSION'),
('grpc_tools\\_protoc_compiler.cp314-win_amd64.pyd', ('grpc_tools\\_protoc_compiler.cp313-win_amd64.pyd',
'C:\\Python314\\Lib\\site-packages\\grpc_tools\\_protoc_compiler.cp314-win_amd64.pyd', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc_tools\\_protoc_compiler.cp313-win_amd64.pyd',
'EXTENSION'), 'EXTENSION'),
('api-ms-win-crt-filesystem-l1-1-0.dll', ('api-ms-win-crt-process-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-filesystem-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-process-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-locale-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-time-l1-1-0.dll', ('api-ms-win-crt-time-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-time-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-time-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-environment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-runtime-l1-1-0.dll', ('api-ms-win-crt-runtime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-runtime-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-runtime-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll', ('api-ms-win-crt-locale-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-convert-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-heap-l1-1-0.dll', ('api-ms-win-crt-heap-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-heap-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-heap-l1-1-0.dll',
'BINARY'), 'BINARY'),
('zlib.dll', 'C:\\Users\\Administrator\\miniconda3\\zlib.dll', 'BINARY'),
('api-ms-win-crt-string-l1-1-0.dll', ('api-ms-win-crt-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-string-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-string-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-process-l1-1-0.dll', ('api-ms-win-crt-filesystem-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-process-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-filesystem-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-math-l1-1-0.dll', ('api-ms-win-crt-environment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-math-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-convert-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-stdio-l1-1-0.dll', ('api-ms-win-crt-stdio-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-stdio-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-stdio-l1-1-0.dll',
@@ -150,184 +181,217 @@
('api-ms-win-crt-conio-l1-1-0.dll', ('api-ms-win-crt-conio-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-conio-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-conio-l1-1-0.dll',
'BINARY'), 'BINARY'),
('VCRUNTIME140.dll', 'C:\\Python314\\VCRUNTIME140.dll', 'BINARY'), ('VCRUNTIME140.dll',
('VCRUNTIME140_1.dll', 'C:\\Python314\\VCRUNTIME140_1.dll', 'BINARY'), 'C:\\Users\\Administrator\\miniconda3\\VCRUNTIME140.dll',
('api-ms-win-crt-utility-l1-1-0.dll', 'BINARY'),
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-utility-l1-1-0.dll', ('api-ms-win-crt-math-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-math-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-crt-private-l1-1-0.dll', ('api-ms-win-crt-private-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-private-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-private-l1-1-0.dll',
'BINARY'), 'BINARY'),
('libcrypto-3.dll', 'C:\\Python314\\DLLs\\libcrypto-3.dll', 'BINARY'), ('api-ms-win-crt-utility-l1-1-0.dll',
('libssl-3.dll', 'C:\\Python314\\DLLs\\libssl-3.dll', 'BINARY'), 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-crt-utility-l1-1-0.dll',
('libffi-8.dll', 'C:\\Python314\\DLLs\\libffi-8.dll', 'BINARY'), 'BINARY'),
('python3.dll', 'C:\\Python314\\python3.dll', 'BINARY'), ('VCRUNTIME140_1.dll',
'C:\\Users\\Administrator\\miniconda3\\VCRUNTIME140_1.dll',
'BINARY'),
('libexpat.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libexpat.dll',
'BINARY'),
('libssl-3-x64.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libssl-3-x64.dll',
'BINARY'),
('libcrypto-3-x64.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libcrypto-3-x64.dll',
'BINARY'),
('libmpdec-4.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\libmpdec-4.dll',
'BINARY'),
('liblzma.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\liblzma.dll',
'BINARY'),
('LIBBZ2.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\LIBBZ2.dll',
'BINARY'),
('ffi.dll',
'C:\\Users\\Administrator\\miniconda3\\Library\\bin\\ffi.dll',
'BINARY'),
('python3.dll',
'C:\\Users\\Administrator\\miniconda3\\python3.dll',
'BINARY'),
('ucrtbase.dll', ('ucrtbase.dll',
'C:\\Users\\Administrator\\miniconda3\\ucrtbase.dll', 'C:\\Users\\Administrator\\miniconda3\\ucrtbase.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-1.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'),
('api-ms-win-core-errorhandling-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-debug-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-timezone-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-console-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-2-0.dll', ('api-ms-win-core-synch-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-2-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-2-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll', ('api-ms-win-core-errorhandling-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-interlocked-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll', ('api-ms-win-core-console-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-handle-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll', ('api-ms-win-core-processthreads-l1-1-1.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-libraryloader-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-fibers-l1-1-0.dll', ('api-ms-win-core-debug-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-fibers-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll', ('api-ms-win-core-string-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-heap-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-file-l1-1-0.dll', ('api-ms-win-core-file-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll', ('api-ms-win-core-timezone-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-sysinfo-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-processenvironment-l1-1-0.dll', ('api-ms-win-core-processenvironment-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processenvironment-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processenvironment-l1-1-0.dll',
'BINARY'), 'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-sysinfo-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-interlocked-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-libraryloader-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-handle-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-1-0.dll', ('api-ms-win-core-synch-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-1-0.dll', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-synch-l1-1-0.dll',
'BINARY'), 'BINARY'),
('grpc\\_cython\\_credentials\\roots.pem', ('api-ms-win-core-fibers-l1-1-0.dll',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_credentials\\roots.pem', 'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-fibers-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'C:\\Users\\Administrator\\miniconda3\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('sensor_stream_pb2.py',
'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_stream_pb2.py',
'DATA'), 'DATA'),
('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h', ('sensor_stream_pb2_grpc.py',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\sensor_stream_pb2_grpc.py',
'DATA'), 'DATA'),
('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc', ('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc',
'C:\\Python314\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.cc',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING', ('grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_cygrpc\\private_key_signing\\private_key_signer_py_wrapper.h',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\INSTALLER', ('grpc\\_cython\\_credentials\\roots.pem',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\INSTALLER', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\grpc\\_cython\\_credentials\\roots.pem',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\REQUESTED', ('numpy-2.4.6.dist-info\\WHEEL',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\REQUESTED', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\WHEEL',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md', ('numpy-2.4.6.dist-info\\REQUESTED',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\REQUESTED',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\linalg\\lapack_lite\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\RECORD', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\RECORD', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\WHEEL', ('numpy-2.4.6.dist-info\\INSTALLER',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\WHEEL', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\INSTALLER',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\sfc64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\fft\\pocketfft\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\ma\\LICENSE',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\ma\\LICENSE',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\ma\\LICENSE', ('numpy-2.4.6.dist-info\\entry_points.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\ma\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\entry_points.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\common\\pythoncapi-compat\\COPYING',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\entry_points.txt', ('numpy-2.4.6.dist-info\\licenses\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\entry_points.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\mt19937\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\splitmix64\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\umath\\svml\\LICENSE',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\multiarray\\dragon4_LICENSE.txt', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\include\\numpy\\libdivide\\LICENSE.txt',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\METADATA', ('numpy-2.4.6.dist-info\\DELVEWHEEL',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\METADATA', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\DELVEWHEEL',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\DELVEWHEEL', ('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\DELVEWHEEL', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE', ('numpy-2.4.6.dist-info\\METADATA',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\METADATA',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md', ('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\random\\src\\distributions\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\philox\\LICENSE.md',
'DATA'), 'DATA'),
('numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md', ('numpy-2.4.6.dist-info\\RECORD',
'C:\\Python314\\Lib\\site-packages\\numpy-2.4.4.dist-info\\licenses\\numpy\\_core\\src\\npysort\\x86-simd-sort\\LICENSE.md', 'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\RECORD',
'DATA'),
('numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\_core\\src\\highway\\LICENSE',
'DATA'),
('numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md',
'C:\\Users\\Administrator\\miniconda3\\Lib\\site-packages\\numpy-2.4.6.dist-info\\licenses\\numpy\\random\\src\\pcg64\\LICENSE.md',
'DATA'), 'DATA'),
('base_library.zip', ('base_library.zip',
'd:\\JE-Skin-main\\devkit\\build\\je-skin-devkit-server\\base_library.zip', 'D:\\Workspace\\je-skin-customer-demo\\devkit\\build\\je-skin-devkit-server\\base_library.zip',
'DATA')], 'DATA')],
'python314.dll', 'python313.dll',
False, False,
False, False,
False, False,

File diff suppressed because it is too large Load Diff

View File

@@ -14,40 +14,41 @@ Types of import:
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks! tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (optional), netrc (delayed, optional), subprocess (delayed, conditional, optional), http.server (delayed, optional) missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional), http.server (delayed, optional), netrc (delayed, optional), getpass (delayed, optional)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (optional), subprocess (delayed, conditional, optional) missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib._local (optional), subprocess (delayed, conditional, optional)
missing module named 'collections.abc' - imported by _colorize (top-level), typing (top-level), traceback (top-level), logging (top-level), selectors (top-level), http.client (top-level), importlib.resources.readers (top-level), inspect (top-level), tracemalloc (top-level), multiprocessing.managers (top-level), typing_extensions (top-level), asyncio.base_events (top-level), asyncio.coroutines (top-level), grpc.aio._metadata (top-level), google.protobuf.internal.containers (top-level), google.protobuf.internal.well_known_types (top-level), numpy._typing._array_like (top-level), numpy._typing._nested_sequence (conditional), numpy._typing._shape (top-level), numpy._typing._dtype_like (top-level), numpy.lib._function_base_impl (top-level), _pyrepl.types (top-level), numpy.lib._npyio_impl (top-level), numpy.random._common (top-level), numpy.random._generator (top-level), numpy.random.bit_generator (top-level), numpy.random.mtrand (top-level), numpy.polynomial._polybase (top-level), xml.etree.ElementTree (top-level) missing module named 'collections.abc' - imported by traceback (top-level), typing (top-level), inspect (top-level), logging (top-level), importlib.resources.readers (top-level), selectors (top-level), tracemalloc (top-level), http.client (top-level), typing_extensions (top-level), asyncio.base_events (top-level), asyncio.coroutines (top-level), grpc.aio._metadata (top-level), google.protobuf.internal.containers (top-level), google.protobuf.internal.well_known_types (top-level), numpy._typing._array_like (top-level), numpy._typing._nested_sequence (conditional), numpy._typing._shape (top-level), numpy._typing._dtype_like (top-level), numpy.lib._function_base_impl (top-level), numpy.lib._npyio_impl (top-level), numpy.random._common (top-level), numpy.random._generator (top-level), numpy.random.bit_generator (top-level), numpy.random.mtrand (top-level), numpy.polynomial._polybase (top-level), xml.etree.ElementTree (top-level)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed) missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
missing module named fcntl - imported by pathlib._os (optional), subprocess (optional) missing module named fcntl - imported by subprocess (optional)
missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional) missing module named _posixshmem - imported by multiprocessing.resource_tracker (conditional), multiprocessing.shared_memory (conditional)
missing module named _scproxy - imported by urllib.request (conditional) missing module named _scproxy - imported by urllib.request (conditional)
missing module named posix - imported by posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional), pathlib._os (optional), os (conditional, optional), _pyrepl.trace (conditional) missing module named termios - imported by tty (top-level), _pyrepl.pager (delayed, optional), getpass (optional)
missing module named resource - imported by posix (top-level) missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.connection (top-level)
missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level) missing module named _frozen_importlib_external - imported by importlib._bootstrap (delayed), importlib (optional), importlib.abc (optional), zipimport (top-level)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level) excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named multiprocessing.BufferTooShort - imported by multiprocessing (top-level), multiprocessing.connection (top-level) missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional)
missing module named multiprocessing.AuthenticationError - imported by multiprocessing (top-level), multiprocessing.forkserver (top-level), multiprocessing.connection (top-level) missing module named resource - imported by posix (top-level)
missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level) missing module named multiprocessing.get_context - imported by multiprocessing (top-level), multiprocessing.pool (top-level), multiprocessing.managers (top-level), multiprocessing.sharedctypes (top-level)
missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level) missing module named multiprocessing.TimeoutError - imported by multiprocessing (top-level), multiprocessing.pool (top-level)
missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level) missing module named multiprocessing.set_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level) missing module named multiprocessing.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named pyimod02_importers - imported by C:\Python314\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed) missing module named pyimod02_importers - imported by C:\Users\Administrator\miniconda3\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional) missing module named _dummy_thread - imported by numpy._core.arrayprint (optional)
missing module named asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named annotationlib - imported by typing_extensions (conditional)
missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional) missing module named 'numpy_distutils.cpuinfo' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional) missing module named 'numpy_distutils.fcompiler' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named 'numpy_distutils.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional) missing module named 'numpy_distutils.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional) missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional)
missing module named charset_normalizer - imported by numpy.f2py.crackfortran (optional)
missing module named vms_lib - imported by platform (delayed, optional) missing module named vms_lib - imported by platform (delayed, optional)
missing module named 'java.lang' - imported by platform (delayed, optional) missing module named 'java.lang' - imported by platform (delayed, optional)
missing module named java - imported by platform (delayed) missing module named java - imported by platform (delayed)
missing module named psutil - imported by numpy.testing._private.utils (delayed, optional) missing module named psutil - imported by numpy.testing._private.utils (delayed, optional)
missing module named termios - imported by tty (top-level), _pyrepl.pager (delayed, optional)
missing module named readline - imported by cmd (delayed, conditional, optional), code (delayed, conditional, optional), pdb (delayed, conditional, optional), rlcompleter (optional) missing module named readline - imported by cmd (delayed, conditional, optional), code (delayed, conditional, optional), pdb (delayed, conditional, optional), rlcompleter (optional)
missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional) missing module named win32pdh - imported by numpy.testing._private.utils (delayed, conditional)
missing module named _typeshed - imported by numpy.random.bit_generator (top-level) missing module named _typeshed - imported by numpy.random.bit_generator (top-level)
missing module named numpy.random.RandomState - imported by numpy.random (top-level), numpy.random._generator (top-level) missing module named numpy.random.RandomState - imported by numpy.random (top-level), numpy.random._generator (top-level)
missing module named pyodide_js - imported by threadpoolctl (delayed, optional) missing module named threadpoolctl - imported by numpy.lib._utils_impl (delayed, optional)
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional) missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.vstack - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional) missing module named numpy._core.vstack - imported by numpy._core (top-level), numpy.lib._shape_base_impl (top-level), numpy (conditional)
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional) missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
@@ -84,7 +85,7 @@ missing module named numpy._core.sinh - imported by numpy._core (conditional), n
missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional) missing module named numpy._core.single - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional) missing module named numpy._core.signedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.signbit - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional) missing module named numpy._core.signbit - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.sign - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional) missing module named numpy._core.sign - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional) missing module named numpy._core.short - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional) missing module named numpy._core.rint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional) missing module named numpy._core.right_shift - imported by numpy._core (conditional), numpy (conditional)

Binary file not shown.

Binary file not shown.

View File

@@ -5,8 +5,8 @@ a = Analysis(
['sensor_server.py'], ['sensor_server.py'],
pathex=[], pathex=[],
binaries=[], binaries=[],
datas=[], datas=[('sensor_stream_pb2*.py', '.')],
hiddenimports=[], hiddenimports=['grpc', 'openpyxl'],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},
runtime_hooks=[], runtime_hooks=[],

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@ _sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13sensor_stream.proto\x12\rsensor_stream\"\x85\x01\n\x0bSensorFrame\x12\x0b\n\x03seq\x18\x01 \x01(\x04\x12\x14\n\x0ctimestamp_ms\x18\x02 \x01(\x04\x12\x0c\n\x04rows\x18\x03 \x01(\r\x12\x0c\n\x04\x63ols\x18\x04 \x01(\r\x12\x0e\n\x06matrix\x18\x05 \x03(\r\x12\x17\n\x0fresultant_force\x18\x06 \x01(\x01\x12\x0e\n\x06\x64ts_ms\x18\x07 \x01(\r\"\xd1\x01\n\x10PztAngleResponse\x12\x0b\n\x03seq\x18\x01 \x01(\x04\x12\x14\n\x0ctimestamp_ms\x18\x02 \x01(\x04\x12\r\n\x05\x61ngle\x18\x03 \x01(\x02\x12\x0e\n\x06\x64ts_ms\x18\x04 \x01(\r\x12\n\n\x02ok\x18\x05 \x01(\x08\x12\x0f\n\x07message\x18\x06 \x01(\t\x12\x11\n\tmagnitude\x18\x07 \x01(\x02\x12\r\n\x05state\x18\x08 \x01(\r\x12\r\n\x05\x63op_x\x18\t \x01(\x02\x12\r\n\x05\x63op_y\x18\n \x01(\x02\x12\x0e\n\x06\x62\x61se_x\x18\x0b \x01(\x02\x12\x0e\n\x06\x62\x61se_y\x18\x0c \x01(\x02\"8\n\x0eProcessRequest\x12\x10\n\x08\x63sv_path\x18\x01 \x01(\t\x12\x14\n\x0csave_as_xlsx\x18\x02 \x01(\x08\"\xa6\x01\n\x0fProcessResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x0boutput_path\x18\x02 \x01(\t\x12\x13\n\x0bgroups_used\x18\x03 \x01(\r\x12\x12\n\nmean_value\x18\x04 \x01(\x01\x12\x11\n\tthreshold\x18\x05 \x01(\x01\x12\x12\n\nrows_total\x18\x06 \x01(\r\x12\x11\n\trows_kept\x18\x07 \x01(\r\x12\x0f\n\x07message\x18\x08 \x01(\t2W\n\nSensorPush\x12I\n\x06Upload\x12\x1a.sensor_stream.SensorFrame\x1a\x1f.sensor_stream.PztAngleResponse(\x01\x30\x01\x32_\n\x0f\x45xportProcessor\x12L\n\x0bProcessFile\x12\x1d.sensor_stream.ProcessRequest\x1a\x1e.sensor_stream.ProcessResponseb\x06proto3') DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13sensor_stream.proto\x12\rsensor_stream\"\x85\x01\n\x0bSensorFrame\x12\x0b\n\x03seq\x18\x01 \x01(\x04\x12\x14\n\x0ctimestamp_ms\x18\x02 \x01(\x04\x12\x0c\n\x04rows\x18\x03 \x01(\r\x12\x0c\n\x04\x63ols\x18\x04 \x01(\r\x12\x0e\n\x06matrix\x18\x05 \x03(\r\x12\x17\n\x0fresultant_force\x18\x06 \x01(\x01\x12\x0e\n\x06\x64ts_ms\x18\x07 \x01(\r\"\xf9\x01\n\x10PztAngleResponse\x12\x0b\n\x03seq\x18\x01 \x01(\x04\x12\x14\n\x0ctimestamp_ms\x18\x02 \x01(\x04\x12\r\n\x05\x61ngle\x18\x03 \x01(\x02\x12\x0e\n\x06\x64ts_ms\x18\x04 \x01(\r\x12\n\n\x02ok\x18\x05 \x01(\x08\x12\x0f\n\x07message\x18\x06 \x01(\t\x12\x11\n\tmagnitude\x18\x07 \x01(\x02\x12\r\n\x05state\x18\x08 \x01(\x05\x12\r\n\x05\x63op_x\x18\t \x01(\x02\x12\r\n\x05\x63op_y\x18\n \x01(\x02\x12\x0e\n\x06\x62\x61se_x\x18\x0b \x01(\x02\x12\x0e\n\x06\x62\x61se_y\x18\x0c \x01(\x02\x12\x13\n\x0btotal_press\x18\r \x01(\x02\x12\x11\n\tthreshold\x18\x0e \x01(\x02\"8\n\x0eProcessRequest\x12\x10\n\x08\x63sv_path\x18\x01 \x01(\t\x12\x14\n\x0csave_as_xlsx\x18\x02 \x01(\x08\"\xa6\x01\n\x0fProcessResponse\x12\n\n\x02ok\x18\x01 \x01(\x08\x12\x13\n\x0boutput_path\x18\x02 \x01(\t\x12\x13\n\x0bgroups_used\x18\x03 \x01(\r\x12\x12\n\nmean_value\x18\x04 \x01(\x01\x12\x11\n\tthreshold\x18\x05 \x01(\x01\x12\x12\n\nrows_total\x18\x06 \x01(\r\x12\x11\n\trows_kept\x18\x07 \x01(\r\x12\x0f\n\x07message\x18\x08 \x01(\t2W\n\nSensorPush\x12I\n\x06Upload\x12\x1a.sensor_stream.SensorFrame\x1a\x1f.sensor_stream.PztAngleResponse(\x01\x30\x01\x32_\n\x0f\x45xportProcessor\x12L\n\x0bProcessFile\x12\x1d.sensor_stream.ProcessRequest\x1a\x1e.sensor_stream.ProcessResponseb\x06proto3')
_globals = globals() _globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -34,13 +34,13 @@ if not _descriptor._USE_C_DESCRIPTORS:
_globals['_SENSORFRAME']._serialized_start=39 _globals['_SENSORFRAME']._serialized_start=39
_globals['_SENSORFRAME']._serialized_end=172 _globals['_SENSORFRAME']._serialized_end=172
_globals['_PZTANGLERESPONSE']._serialized_start=175 _globals['_PZTANGLERESPONSE']._serialized_start=175
_globals['_PZTANGLERESPONSE']._serialized_end=384 _globals['_PZTANGLERESPONSE']._serialized_end=424
_globals['_PROCESSREQUEST']._serialized_start=386 _globals['_PROCESSREQUEST']._serialized_start=426
_globals['_PROCESSREQUEST']._serialized_end=442 _globals['_PROCESSREQUEST']._serialized_end=482
_globals['_PROCESSRESPONSE']._serialized_start=445 _globals['_PROCESSRESPONSE']._serialized_start=485
_globals['_PROCESSRESPONSE']._serialized_end=611 _globals['_PROCESSRESPONSE']._serialized_end=651
_globals['_SENSORPUSH']._serialized_start=613 _globals['_SENSORPUSH']._serialized_start=653
_globals['_SENSORPUSH']._serialized_end=700 _globals['_SENSORPUSH']._serialized_end=740
_globals['_EXPORTPROCESSOR']._serialized_start=702 _globals['_EXPORTPROCESSOR']._serialized_start=742
_globals['_EXPORTPROCESSOR']._serialized_end=797 _globals['_EXPORTPROCESSOR']._serialized_end=837
# @@protoc_insertion_point(module_scope) # @@protoc_insertion_point(module_scope)

View File

33231
devkit/server-start.out.log Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,127 +0,0 @@
"""
独立测试脚本读取84个原始ADC数据传入CoP算法计算角度终端打印结果。
用法:
python test_pzt.py # 从 stdin 逐行读取每行84个逗号分隔数值
python test_pzt.py data.csv # 从 CSV 文件逐行读取
python test_pzt.py --random # 生成随机测试数据(调试用)
"""
import sys
import csv
import numpy as np
# ── 从 sensor_server.py 导入算法 ──
sys.path.insert(0, ".")
from sensor_server import (
get_pzt_angle,
reset_baseline,
subtract_baseline,
compute_pressure_direction,
compute_PZT_angle,
)
def print_result(data_label: str, pzt_angle: float, magnitude: float, state: int, cop_x: float, cop_y: float, base_x: float, base_y: float):
dx = cop_x - base_x
dy = base_y - cop_y
print(
f"devkit: angle={pzt_angle:.2f}, magnitude={magnitude:.4f}, state={state}, "
f"cop_x={cop_x:.4f}, cop_y={cop_y:.4f}, dx={dx:.4f}, dy={dy:.4f}"
)
def process_values(values: list[int | float]):
"""处理一帧84个值并打印结果"""
if len(values) != 84:
print(f"[ERROR] 期望84个值实际收到 {len(values)}", file=sys.stderr)
return
try:
pzt_angle, magnitude, state, cop_x, cop_y, base_x, base_y = get_pzt_angle(values)
print_result("", pzt_angle, magnitude, state, cop_x, cop_y, base_x, base_y)
except Exception as e:
print(f"[ERROR] 计算失败: {e}", file=sys.stderr)
def run_random_test():
"""生成随机数据测试算法"""
reset_baseline()
print("[TEST] 使用随机数据测试 CoP 算法")
print("[TEST] 先用全零帧建立基线...")
process_values([0] * 84)
print("[TEST] 模拟右侧偏移按压...")
# 模拟row 5-7, col 4-6 区域有压力
data = [0.0] * 84
for r in range(5, 8):
for c in range(4, 7):
idx = r * 7 + c
data[idx] = 100.0 + (c - 4) * 50 # 右侧更强
process_values(data)
print("[TEST] 模拟下方偏移按压...")
data2 = [0.0] * 84
for r in range(8, 11):
for c in range(2, 5):
idx = r * 7 + c
data2[idx] = 150.0 + (r - 8) * 30
process_values(data2)
print("[TEST] 完成")
def run_csv_mode(filepath: str):
"""从 CSV 文件逐行读取并处理"""
reset_baseline()
print(f"[CSV] 读取文件: {filepath}")
with open(filepath, "r", encoding="utf-8-sig", newline="") as f:
reader = csv.reader(f)
for i, row in enumerate(reader):
if not row:
continue
# 跳过 header
if row[0].strip() in ("seq", "timestamp_ms"):
print(f"[CSV] 跳过 header: {row[:5]}...")
continue
try:
values = [float(v) for v in row]
if len(values) == 84:
process_values(values)
elif len(values) > 84:
process_values(values[:84])
except ValueError:
continue
def run_stdin_mode():
"""从 stdin 逐行读取"""
reset_baseline()
print("[STDIN] 等待输入每行84个逗号分隔数值Ctrl+C 退出)...")
try:
for line in sys.stdin:
line = line.strip()
if not line:
continue
try:
values = [float(v) for v in line.split(",")]
if len(values) >= 84:
process_values(values[:84])
except ValueError:
continue
except KeyboardInterrupt:
print("\n[STDIN] 已退出")
def main():
if len(sys.argv) > 1:
arg = sys.argv[1]
if arg == "--random":
run_random_test()
elif arg == "--help" or arg == "-h":
print(__doc__)
else:
run_csv_mode(arg)
else:
run_stdin_mode()
if __name__ == "__main__":
main()

32
docs/CHANGELOG.md Normal file
View File

@@ -0,0 +1,32 @@
# 更新日志
## 2026-06-09 - 送样分支
分支:`sample-delivery`
提交:`160ff54 Prepare sample delivery UI`
### 显示与交互
- 合力曲线改为 Canvas 渲染,保留原有 HUD 风格。
- 合力曲线 panel 宽度调整为与三维力 panel 一致。
- 卸力后合力曲线继续接收并渲染后端 0 值数据panel 延迟 5 秒消失。
- 5 秒内重新施力会取消合力曲线的消失计时。
- 三维力 panel 改为卸力后延迟 5 秒消失。
- 三维力箭头仍然实时跟随后端数据,卸力后箭头立即消失,不冻结最后方向。
### 后端数据
- 合力低于显示阈值时,后端不再清空 summary 数组,而是持续写入 `0.0`
- 压力矩阵在低于显示阈值时仍然清零。
- 前端可以通过 summary 数组是否存在和最新值大小来判断释放状态。
### 显示范围
- 合力与压力数值显示增加最大显示值处理:超过 `25.6` 时显示为 `25.6+`
### 验证
- `npm.cmd run check` 通过0 errors保留 13 个既有 warnings。
- `cargo check` 通过。

104
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "0.4.0", "version": "0.4.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.10.1", "@tauri-apps/plugin-updater": "^2.10.1",
@@ -19,7 +19,7 @@
"@sveltejs/adapter-static": "^3.0.6", "@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.9.0", "@sveltejs/kit": "^2.9.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tauri-apps/cli": "^2", "@tauri-apps/cli": "^2.11.2",
"@types/three": "^0.183.1", "@types/three": "^0.183.1",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
@@ -1032,9 +1032,9 @@
} }
}, },
"node_modules/@tauri-apps/api": { "node_modules/@tauri-apps/api": {
"version": "2.10.1", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.11.0.tgz",
"integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==", "integrity": "sha512-7CinYODhky9lmO23xHnUFv0Xt43fbtWMyxZcLcRBlFkcgXKuEirBvHpmtJ89YMhyeGcq20Wuc47Fa4XjyniywA==",
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"funding": { "funding": {
"type": "opencollective", "type": "opencollective",
@@ -1042,9 +1042,9 @@
} }
}, },
"node_modules/@tauri-apps/cli": { "node_modules/@tauri-apps/cli": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz",
"integrity": "sha512-jQNGF/5quwORdZSSLtTluyKQ+o6SMa/AUICfhf4egCGFdMHqWssApVgYSbg+jmrZoc8e1DscNvjTnXtlHLS11g==", "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==",
"dev": true, "dev": true,
"license": "Apache-2.0 OR MIT", "license": "Apache-2.0 OR MIT",
"bin": { "bin": {
@@ -1058,23 +1058,23 @@
"url": "https://opencollective.com/tauri" "url": "https://opencollective.com/tauri"
}, },
"optionalDependencies": { "optionalDependencies": {
"@tauri-apps/cli-darwin-arm64": "2.10.1", "@tauri-apps/cli-darwin-arm64": "2.11.2",
"@tauri-apps/cli-darwin-x64": "2.10.1", "@tauri-apps/cli-darwin-x64": "2.11.2",
"@tauri-apps/cli-linux-arm-gnueabihf": "2.10.1", "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2",
"@tauri-apps/cli-linux-arm64-gnu": "2.10.1", "@tauri-apps/cli-linux-arm64-gnu": "2.11.2",
"@tauri-apps/cli-linux-arm64-musl": "2.10.1", "@tauri-apps/cli-linux-arm64-musl": "2.11.2",
"@tauri-apps/cli-linux-riscv64-gnu": "2.10.1", "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2",
"@tauri-apps/cli-linux-x64-gnu": "2.10.1", "@tauri-apps/cli-linux-x64-gnu": "2.11.2",
"@tauri-apps/cli-linux-x64-musl": "2.10.1", "@tauri-apps/cli-linux-x64-musl": "2.11.2",
"@tauri-apps/cli-win32-arm64-msvc": "2.10.1", "@tauri-apps/cli-win32-arm64-msvc": "2.11.2",
"@tauri-apps/cli-win32-ia32-msvc": "2.10.1", "@tauri-apps/cli-win32-ia32-msvc": "2.11.2",
"@tauri-apps/cli-win32-x64-msvc": "2.10.1" "@tauri-apps/cli-win32-x64-msvc": "2.11.2"
} }
}, },
"node_modules/@tauri-apps/cli-darwin-arm64": { "node_modules/@tauri-apps/cli-darwin-arm64": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz",
"integrity": "sha512-Z2OjCXiZ+fbYZy7PmP3WRnOpM9+Fy+oonKDEmUE6MwN4IGaYqgceTjwHucc/kEEYZos5GICve35f7ZiizgqEnQ==", "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1089,9 +1089,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-darwin-x64": { "node_modules/@tauri-apps/cli-darwin-x64": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz",
"integrity": "sha512-V/irQVvjPMGOTQqNj55PnQPVuH4VJP8vZCN7ajnj+ZS8Kom1tEM2hR3qbbIRoS3dBKs5mbG8yg1WC+97dq17Pw==", "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1106,9 +1106,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz",
"integrity": "sha512-Hyzwsb4VnCWKGfTw+wSt15Z2pLw2f0JdFBfq2vHBOBhvg7oi6uhKiF87hmbXOBXUZaGkyRDkCHsdzJcIfoJC2w==", "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==",
"cpu": [ "cpu": [
"arm" "arm"
], ],
@@ -1123,9 +1123,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-gnu": { "node_modules/@tauri-apps/cli-linux-arm64-gnu": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz",
"integrity": "sha512-OyOYs2t5GkBIvyWjA1+h4CZxTcdz1OZPCWAPz5DYEfB0cnWHERTnQ/SLayQzncrT0kwRoSfSz9KxenkyJoTelA==", "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1143,9 +1143,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-arm64-musl": { "node_modules/@tauri-apps/cli-linux-arm64-musl": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz",
"integrity": "sha512-MIj78PDDGjkg3NqGptDOGgfXks7SYJwhiMh8SBoZS+vfdz7yP5jN18bNaLnDhsVIPARcAhE1TlsZe/8Yxo2zqg==", "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1163,9 +1163,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": { "node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz",
"integrity": "sha512-X0lvOVUg8PCVaoEtEAnpxmnkwlE1gcMDTqfhbefICKDnOTJ5Est3qL0SrWxizDackIOKBcvtpejrSiVpuJI1kw==", "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==",
"cpu": [ "cpu": [
"riscv64" "riscv64"
], ],
@@ -1183,9 +1183,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-gnu": { "node_modules/@tauri-apps/cli-linux-x64-gnu": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz",
"integrity": "sha512-2/12bEzsJS9fAKybxgicCDFxYD1WEI9kO+tlDwX5znWG2GwMBaiWcmhGlZ8fi+DMe9CXlcVarMTYc0L3REIRxw==", "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1203,9 +1203,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-linux-x64-musl": { "node_modules/@tauri-apps/cli-linux-x64-musl": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz",
"integrity": "sha512-Y8J0ZzswPz50UcGOFuXGEMrxbjwKSPgXftx5qnkuMs2rmwQB5ssvLb6tn54wDSYxe7S6vlLob9vt0VKuNOaCIQ==", "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==",
"cpu": [ "cpu": [
"x64" "x64"
], ],
@@ -1223,9 +1223,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-arm64-msvc": { "node_modules/@tauri-apps/cli-win32-arm64-msvc": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz",
"integrity": "sha512-iSt5B86jHYAPJa/IlYw++SXtFPGnWtFJriHn7X0NFBVunF6zu9+/zOn8OgqIWSl8RgzhLGXQEEtGBdR4wzpVgg==", "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==",
"cpu": [ "cpu": [
"arm64" "arm64"
], ],
@@ -1240,9 +1240,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-ia32-msvc": { "node_modules/@tauri-apps/cli-win32-ia32-msvc": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz",
"integrity": "sha512-gXyxgEzsFegmnWywYU5pEBURkcFN/Oo45EAwvZrHMh+zUSEAvO5E8TXsgPADYm31d1u7OQU3O3HsYfVBf2moHw==", "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==",
"cpu": [ "cpu": [
"ia32" "ia32"
], ],
@@ -1257,9 +1257,9 @@
} }
}, },
"node_modules/@tauri-apps/cli-win32-x64-msvc": { "node_modules/@tauri-apps/cli-win32-x64-msvc": {
"version": "2.10.1", "version": "2.11.2",
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.1.tgz", "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz",
"integrity": "sha512-6Cn7YpPFwzChy0ERz6djKEmUehWrYlM+xTaNzGPgZocw3BD7OfwfWHKVWxXzdjEW2KfKkHddfdxK1XXTYqBRLg==", "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==",
"cpu": [ "cpu": [
"x64" "x64"
], ],

View File

@@ -15,7 +15,7 @@
}, },
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@tauri-apps/api": "^2", "@tauri-apps/api": "^2.11.0",
"@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-opener": "^2",
"@tauri-apps/plugin-process": "^2.3.1", "@tauri-apps/plugin-process": "^2.3.1",
"@tauri-apps/plugin-updater": "^2.10.1", "@tauri-apps/plugin-updater": "^2.10.1",
@@ -25,7 +25,7 @@
"@sveltejs/adapter-static": "^3.0.6", "@sveltejs/adapter-static": "^3.0.6",
"@sveltejs/kit": "^2.9.0", "@sveltejs/kit": "^2.9.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0", "@sveltejs/vite-plugin-svelte": "^5.0.0",
"@tauri-apps/cli": "^2", "@tauri-apps/cli": "^2.11.2",
"@types/three": "^0.183.1", "@types/three": "^0.183.1",
"svelte": "^5.0.0", "svelte": "^5.0.0",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",

1312
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ crate-type = ["staticlib", "cdylib", "rlib"]
[features] [features]
default = ["multi-dim"] default = ["multi-dim"]
debug = []
devkit = ["dep:tonic", "dep:prost", "dep:prost-types", "dep:async-stream", "dep:dirs"] devkit = ["dep:tonic", "dep:prost", "dep:prost-types", "dep:async-stream", "dep:dirs"]
multi-dim = ["dep:ndarray"] multi-dim = ["dep:ndarray"]

View File

@@ -28,7 +28,7 @@ message PztAngleResponse {
bool ok = 5; bool ok = 5;
string message = 6; string message = 6;
float magnitude = 7; float magnitude = 7;
uint32 state = 8; int32 state = 8;
float cop_x = 9; float cop_x = 9;
float cop_y = 10; float cop_y = 10;
float base_x = 11; float base_x = 11;

View File

@@ -1,162 +0,0 @@
/// AD值反解x计算器
/// AD = -5.732*x^3 - 131.5*x^2 + 31980*x + 13490 (x <= 6.57)
/// AD = -377.8*x^2 + 26040*x + 51120 (x > 6.57)
const X_BOUNDARY: f64 = 6.57;
/// 二次方程在边界处的AD值
/// 当 x = 6.57 时AD = -377.8*6.57^2 + 26040*6.57 + 51120
const AD_BOUNDARY: f64 = 205895.10;
/// 二次方程求解器
/// -377.8*x^2 + 26040*x + 51120 = ad
/// 返回 x > 6.57 的那个解
fn solve_quadratic(ad: f64) -> Option<f64> {
let a = -377.8;
let b = 26040.0;
let c = 51120.0 - ad;
let discriminant = b * b - 4.0 * a * c;
if discriminant < 0.0 {
return None;
}
let sqrt_d = discriminant.sqrt();
let x1 = (-b + sqrt_d) / (2.0 * a);
let x2 = (-b - sqrt_d) / (2.0 * a);
// 选择 x > 6.57 的解(只可能有一个解满足这个条件)
if x1 > X_BOUNDARY && x1 > 0.0 {
Some(x1)
} else if x2 > X_BOUNDARY && x2 > 0.0 {
Some(x2)
} else {
None
}
}
/// 计算三次多项式的值
/// f(x) = -5.732*x^3 - 131.5*x^2 + 31980*x + 13490
fn cubic_value(x: f64) -> f64 {
-5.732 * x.powi(3) - 131.5 * x.powi(2) + 31980.0 * x + 13490.0
}
/// 使用二分法求解三次方程 (x <= 6.57)
/// 三次方程在 [0, 6.57] 范围内是单调递增的
fn solve_cubic_bisection(ad: f64) -> Option<f64> {
let mut low = 0.0;
let mut high = X_BOUNDARY;
let target = ad;
// 检查目标是否在范围内
let low_ad = cubic_value(low);
let high_ad = cubic_value(high);
if target < low_ad.min(high_ad) || target > low_ad.max(high_ad) {
return None;
}
for _i in 0..100 {
let mid = (low + high) / 2.0;
let mid_ad = cubic_value(mid);
if (high - low).abs() < 1e-10 {
return Some((low + high) / 2.0);
}
if mid_ad > target {
high = mid;
} else {
low = mid;
}
}
Some((low + high) / 2.0)
}
/// 主求解函数根据AD值反解x
pub fn solve_for_x(ad: f64) -> Option<f64> {
// 如果 AD <= 边界值,使用三次方程 (x <= 6.57)
// 如果 AD > 边界值,使用二次方程 (x > 6.57)
if ad <= AD_BOUNDARY {
return solve_cubic_bisection(ad);
}
// AD > 边界值,使用二次方程
solve_quadratic(ad)
}
/// 批量求解,用于验证所有解
pub fn solve_for_x_all(ad: f64) -> Vec<f64> {
let mut results = Vec::new();
// 三次方程解
if let Some(x) = solve_cubic_bisection(ad) {
results.push(x);
}
// 二次方程解
if let Some(x) = solve_quadratic(ad) {
results.push(x);
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cubic_forward() {
// 测试 x <= 6.57 的正向计算
let x = 5.0;
let ad = cubic_value(x);
println!("x={}, ad={}", x, ad);
let solved = solve_for_x(ad).unwrap();
println!("solved={}", solved);
assert!((solved - x).abs() < 0.01, "x={}, solved={}", x, solved);
}
#[test]
fn test_quadratic_forward() {
// 测试 x > 6.57 的正向计算
let x = 10.0;
let ad = -377.8 * x * x + 26040.0 * x + 51120.0;
let solved = solve_for_x(ad).unwrap();
assert!((solved - x).abs() < 0.01, "x={}, solved={}", x, solved);
}
#[test]
fn test_boundary() {
// 测试边界值
let x = 6.57;
let ad_cubic = cubic_value(x);
let ad_quad = -377.8 * x * x + 26040.0 * x + 51120.0;
println!("x=6.57 时三次方程 AD = {:.2}", ad_cubic);
println!("x=6.57 时二次方程 AD = {:.2}", ad_quad);
println!("边界值 AD_BOUNDARY = {:.2}", AD_BOUNDARY);
// 边界处两个公式应该有显著差异
assert!((ad_cubic - ad_quad).abs() > 100.0);
}
#[test]
fn test_known_values() {
// 测试一些已知值
let test_cases = [
(0.0, cubic_value(0.0)),
(3.0, cubic_value(3.0)),
(6.0, cubic_value(6.0)),
(8.0, -377.8 * 8.0 * 8.0 + 26040.0 * 8.0 + 51120.0),
(15.0, -377.8 * 15.0 * 15.0 + 26040.0 * 15.0 + 51120.0),
];
for (x, ad) in test_cases {
let solved = solve_for_x(ad).unwrap();
assert!((solved - x).abs() < 0.01, "x={}, ad={}, solved={}", x, ad, solved);
}
}
}

View File

@@ -2,11 +2,26 @@
//! //!
//! 仅在 `devkit` feature 启用时编译。 //! 仅在 `devkit` feature 启用时编译。
use serde::Serialize;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
use tauri::State; use tauri::State;
#[cfg(feature = "devkit")] #[cfg(feature = "devkit")]
use tauri::AppHandle; use tauri::AppHandle;
use crate::devkit::{DevKitConfig, DevKitState, DevKitStatusSnapshot, ExportProcessResult}; use crate::devkit::{
proto::SensorFrame, DevKitConfig, DevKitState, DevKitStatusSnapshot, ExportProcessResult,
};
static REPLAY_SEQ_COUNTER: AtomicU64 = AtomicU64::new(0);
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DevKitReplayFramePushResult {
pub seq: u64,
pub timestamp_ms: u64,
pub dts_ms: u32,
}
#[tauri::command] #[tauri::command]
pub fn devkit_status(state: State<'_, DevKitState>) -> DevKitStatusSnapshot { pub fn devkit_status(state: State<'_, DevKitState>) -> DevKitStatusSnapshot {
@@ -51,3 +66,55 @@ pub async fn devkit_process_export(
let use_xlsx = save_as_xlsx.unwrap_or(config.save_as_xlsx); let use_xlsx = save_as_xlsx.unwrap_or(config.save_as_xlsx);
state.process_export(&csv_path, use_xlsx).await state.process_export(&csv_path, use_xlsx).await
} }
#[tauri::command]
pub fn devkit_push_replay_frame(
state: State<'_, DevKitState>,
values: Vec<i32>,
dts_ms: u32,
seq: Option<u64>,
) -> Result<DevKitReplayFramePushResult, String> {
if values.len() != 84 {
return Err(format!("InvalidReplayMatrixLength: {}", values.len()));
}
if !state.running.load(Ordering::Relaxed) {
return Err("NotRunning".to_string());
}
let timestamp_ms = now_millis();
let seq = seq.unwrap_or_else(|| build_replay_seq(timestamp_ms));
let resultant_force = values.iter().copied().sum::<i32>().max(0) as f64;
let matrix = values
.into_iter()
.map(|value| value.max(0) as u32)
.collect::<Vec<_>>();
state.push_frame(SensorFrame {
seq,
timestamp_ms,
rows: 12,
cols: 7,
matrix,
resultant_force,
dts_ms,
});
Ok(DevKitReplayFramePushResult {
seq,
timestamp_ms,
dts_ms,
})
}
fn now_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|duration| duration.as_millis() as u64)
.unwrap_or_default()
}
fn build_replay_seq(timestamp_ms: u64) -> u64 {
let counter = REPLAY_SEQ_COUNTER.fetch_add(1, Ordering::Relaxed) % 1000;
timestamp_ms.saturating_mul(1000).saturating_add(counter)
}

View File

@@ -1,13 +1,15 @@
use crate::serial_core::codecs::hand_gateway::HandGatewayCodec;
use crate::serial_core::codecs::tactile_a::{ use crate::serial_core::codecs::tactile_a::{
export_recording_csv, TactileACodec, TactileACsvImporter, TactileAHandler, export_recording_csv, TactileACodec, TactileACsvImporter, TactileADataPacket, TactileAHandler,
}; };
use crate::serial_core::error::SerialError; use crate::serial_core::error::SerialError;
use crate::serial_core::model::HudSpatialForce;
#[cfg(feature = "multi-dim")]
use crate::serial_core::multi_dim_force::PztProcessor;
use crate::serial_core::record::CsvImporter; use crate::serial_core::record::CsvImporter;
use crate::serial_core::serial::{PollMode, TactileAPollRequester}; use crate::serial_core::serial::{PollMode, TactileAPollRequester};
use crate::serial_core::{serial, TactileARecording}; use crate::serial_core::{serial, TactileARecording};
use log::info; use log::info;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use std::fs::File; use std::fs::File;
use std::io::Cursor; use std::io::Cursor;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -22,10 +24,6 @@ const DEFAULT_TACTILE_ROWS: usize = 12;
const DEFAULT_TACTILE_POLL_INTERVAL_MS: u64 = 10; const DEFAULT_TACTILE_POLL_INTERVAL_MS: u64 = 10;
const DEFAULT_TACTILE_REPLY_TIMEOUT_MS: u64 = 140; const DEFAULT_TACTILE_REPLY_TIMEOUT_MS: u64 = 140;
// MCU currently uses a fixed node layout. Update this list to match the firmware:
// one entry per Config bit, containing that node's number of u16 samples.
const DEFAULT_HAND_GATEWAY_NODE_SAMPLE_COUNTS: &[u16] = &[84];
type SharedTactileRecording = Arc<Mutex<TactileARecording>>; type SharedTactileRecording = Arc<Mutex<TactileARecording>>;
#[derive(Serialize)] #[derive(Serialize)]
@@ -36,13 +34,6 @@ pub struct SerialConnectResponse {
pub message: String, pub message: String,
} }
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum SerialProtocol {
TactileA,
HandGateway,
}
#[derive(Serialize)] #[derive(Serialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct SerialExportResponse { pub struct SerialExportResponse {
@@ -56,6 +47,7 @@ pub struct SerialExportResponse {
pub struct SerialImportFrame { pub struct SerialImportFrame {
pub data: Vec<i32>, pub data: Vec<i32>,
pub dts_ms: u64, pub dts_ms: u64,
pub spatial_force: Option<HudSpatialForce>,
} }
#[derive(Serialize)] #[derive(Serialize)]
@@ -79,7 +71,7 @@ struct SerialSession {
port: String, port: String,
cancel: CancellationToken, cancel: CancellationToken,
task: JoinHandle<()>, task: JoinHandle<()>,
current_record: Option<SharedTactileRecording>, current_record: SharedTactileRecording,
} }
#[derive(Default)] #[derive(Default)]
@@ -90,7 +82,7 @@ pub struct SerialConnectionState {
pub async fn shutdown_active_session( pub async fn shutdown_active_session(
state: &SerialConnectionState, state: &SerialConnectionState,
) -> Result<Option<(String, Option<SharedTactileRecording>)>, SerialError> { ) -> Result<Option<(String, SharedTactileRecording)>, SerialError> {
let session = { let session = {
let mut guard = state.session.lock().map_err(|_| SerialError::StateError)?; let mut guard = state.session.lock().map_err(|_| SerialError::StateError)?;
guard.take() guard.take()
@@ -110,15 +102,13 @@ pub async fn shutdown_active_session(
let _ = task.await; let _ = task.await;
let frame_count = current_record let frame_count = current_record
.as_ref() .lock()
.and_then(|record| record.lock().ok().map(|record| record.frames.len())) .map(|record| record.frames.len())
.unwrap_or(0); .unwrap_or(0);
info!("last_record has {} frames", frame_count); info!("last_record has {} frames", frame_count);
if let (Some(current_record), Ok(mut last_record)) = if let Ok(mut last_record) = state.last_record.lock() {
(current_record.as_ref(), state.last_record.lock())
{
*last_record = Some(current_record.clone()); *last_record = Some(current_record.clone());
} }
@@ -132,8 +122,7 @@ pub fn serial_enum() -> Result<Vec<String>, SerialError> {
.into_iter() .into_iter()
.filter_map(|p| { .filter_map(|p| {
let name = p.port_name; let name = p.port_name;
#[cfg(unix)] if !should_include_serial_port(&name) {
if !name.contains("USB") {
return None; return None;
} }
Some(name) Some(name)
@@ -143,11 +132,25 @@ pub fn serial_enum() -> Result<Vec<String>, SerialError> {
Ok(ports) Ok(ports)
} }
#[cfg(target_os = "macos")]
fn should_include_serial_port(name: &str) -> bool {
name.to_ascii_lowercase().contains("usb")
}
#[cfg(all(unix, not(target_os = "macos")))]
fn should_include_serial_port(name: &str) -> bool {
name.contains("USB")
}
#[cfg(not(unix))]
fn should_include_serial_port(_name: &str) -> bool {
true
}
#[tauri::command] #[tauri::command]
pub async fn serial_connect( pub async fn serial_connect(
app: AppHandle, app: AppHandle,
port: String, port: String,
protocol: SerialProtocol,
state: State<'_, SerialConnectionState>, state: State<'_, SerialConnectionState>,
) -> Result<SerialConnectResponse, SerialError> { ) -> Result<SerialConnectResponse, SerialError> {
let port_name = port.trim().to_string(); let port_name = port.trim().to_string();
@@ -163,10 +166,7 @@ pub async fn serial_connect(
} }
let cancel = CancellationToken::new(); let cancel = CancellationToken::new();
let current_record = match protocol { let current_record = Arc::new(Mutex::new(TactileARecording::new()));
SerialProtocol::TactileA => Some(Arc::new(Mutex::new(TactileARecording::new()))),
SerialProtocol::HandGateway => None,
};
let task_record = current_record.clone(); let task_record = current_record.clone();
let task_cancel = cancel.clone(); let task_cancel = cancel.clone();
let task_app = app.clone(); let task_app = app.clone();
@@ -178,51 +178,32 @@ pub async fn serial_connect(
let session_started_at = Instant::now(); let session_started_at = Instant::now();
let task = tauri::async_runtime::spawn(async move { let task = tauri::async_runtime::spawn(async move {
let run_result = match protocol { let codec = TactileACodec::new(DEFAULT_TACTILE_COLS, DEFAULT_TACTILE_ROWS);
SerialProtocol::TactileA => { let handler = TactileAHandler;
let codec = TactileACodec::new(DEFAULT_TACTILE_COLS, DEFAULT_TACTILE_ROWS); let poll_mode = PollMode::Enabled(Box::new(TactileAPollRequester::new(
let handler = TactileAHandler; Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS),
let poll_mode = PollMode::Enabled(Box::new(TactileAPollRequester::new( DEFAULT_TACTILE_COLS,
Duration::from_millis(DEFAULT_TACTILE_POLL_INTERVAL_MS), DEFAULT_TACTILE_ROWS,
DEFAULT_TACTILE_COLS, Duration::from_millis(DEFAULT_TACTILE_REPLY_TIMEOUT_MS),
DEFAULT_TACTILE_ROWS, )));
Duration::from_millis(DEFAULT_TACTILE_REPLY_TIMEOUT_MS),
)));
serial::run_serial_with_poll( if let Err(error) = serial::run_serial_with_poll(
task_app.clone(), task_app.clone(),
port, port,
codec, codec,
handler, handler,
session_started_at, session_started_at,
task_record task_record.clone(),
.clone() task_cancel,
.expect("tactile protocol must have a recording"), poll_mode,
task_cancel, )
poll_mode, .await
) {
.await log::error!("serial task exited with error: {error}");
}
SerialProtocol::HandGateway => {
let codec = HandGatewayCodec::new(DEFAULT_HAND_GATEWAY_NODE_SAMPLE_COUNTS);
serial::run_hand_gateway_serial(
task_app.clone(),
port,
codec,
session_started_at,
task_cancel,
)
.await
}
};
if let Err(error) = run_result {
eprintln!("serial task exited with error: {error}");
} }
let manager = task_app.state::<SerialConnectionState>(); let manager = task_app.state::<SerialConnectionState>();
if let (Some(task_record), Ok(mut last_record)) = (task_record, manager.last_record.lock()) if let Ok(mut last_record) = manager.last_record.lock() {
{
*last_record = Some(task_record); *last_record = Some(task_record);
} }
@@ -359,13 +340,7 @@ pub fn serial_import_csv(
let channel_count = packets.first().map(|item| item.data.len()).unwrap_or(0); let channel_count = packets.first().map(|item| item.data.len()).unwrap_or(0);
let frame_count = packets.len(); let frame_count = packets.len();
let frames = packets let frames = build_import_frames(packets);
.into_iter()
.map(|packet| SerialImportFrame {
data: packet.data,
dts_ms: packet.dts_ms,
})
.collect();
Ok(SerialImportResponse { Ok(SerialImportResponse {
file_name, file_name,
@@ -390,6 +365,44 @@ pub fn serial_import_csv_from_path(file_path: String) -> Result<SerialImportResp
serial_import_csv(file_name, csv_content) serial_import_csv(file_name, csv_content)
} }
fn build_import_frames(packets: Vec<TactileADataPacket>) -> Vec<SerialImportFrame> {
#[cfg(feature = "multi-dim")]
let mut pzt_processor = PztProcessor::new();
packets
.into_iter()
.map(|packet| {
#[cfg(feature = "multi-dim")]
let spatial_force = replay_spatial_force(&mut pzt_processor, &packet.data);
#[cfg(not(feature = "multi-dim"))]
let spatial_force = None;
SerialImportFrame {
data: packet.data,
dts_ms: packet.dts_ms,
spatial_force,
}
})
.collect()
}
#[cfg(feature = "multi-dim")]
fn replay_spatial_force(processor: &mut PztProcessor, values: &[i32]) -> Option<HudSpatialForce> {
let pzt_values = values.iter().map(|value| *value as f32).collect::<Vec<f32>>();
let analysis = processor.get_pzt_analysis(&pzt_values).ok()?;
if !PztProcessor::should_report(&analysis) {
return None;
}
Some(HudSpatialForce {
angle_deg: analysis.angle_deg,
magnitude: analysis.magnitude,
confidence: analysis.confidence,
})
}
fn resolve_record_for_export( fn resolve_record_for_export(
state: &State<'_, SerialConnectionState>, state: &State<'_, SerialConnectionState>,
) -> Result<SharedTactileRecording, SerialError> { ) -> Result<SharedTactileRecording, SerialError> {
@@ -397,7 +410,7 @@ fn resolve_record_for_export(
let session = state.session.lock().map_err(|_| SerialError::StateError)?; let session = state.session.lock().map_err(|_| SerialError::StateError)?;
session session
.as_ref() .as_ref()
.and_then(|current_session| current_session.current_record.clone()) .map(|current_session| current_session.current_record.clone())
}; };
if let Some(recording) = current_record { if let Some(recording) = current_record {
@@ -418,7 +431,7 @@ fn snapshot_record_frame_count(
let session = state.session.lock().map_err(|_| SerialError::StateError)?; let session = state.session.lock().map_err(|_| SerialError::StateError)?;
session session
.as_ref() .as_ref()
.and_then(|current_session| current_session.current_record.clone()) .map(|current_session| current_session.current_record.clone())
}; };
if let Some(record) = current_record { if let Some(record) = current_record {

View File

@@ -35,6 +35,6 @@ pub async fn win_close(
.await .await
.map_err(|error| error.to_string())?; .map_err(|error| error.to_string())?;
app.exit(0); main_window(&app)?.close().map_err(|error| error.to_string())
Ok(())
} }

View File

@@ -24,6 +24,14 @@ struct DevKitPztAngleEvent {
timestamp_ms: u64, timestamp_ms: u64,
dts_ms: u32, dts_ms: u32,
angle: f32, angle: f32,
magnitude: f32,
state: i32,
cop_x: f32,
cop_y: f32,
base_x: f32,
base_y: f32,
total_press: f32,
threshold: f32,
} }
// ── DevKit 配置 ──────────────────────────────────────────────────── // ── DevKit 配置 ────────────────────────────────────────────────────
@@ -276,9 +284,19 @@ async fn run_grpc_upload(
timestamp_ms: message.timestamp_ms, timestamp_ms: message.timestamp_ms,
dts_ms: message.dts_ms, dts_ms: message.dts_ms,
angle: message.angle, angle: message.angle,
magnitude: message.magnitude,
state: message.state,
cop_x: message.cop_x,
cop_y: message.cop_y,
base_x: message.base_x,
base_y: message.base_y,
total_press: message.total_press,
threshold: message.threshold,
}; };
::log::debug!( ::log::debug!(
"devkit: angle={:.2}, magnitude={:.4}, state={}, cop_x={:.4}, cop_y={:.4}, base_x={:.4}, base_y={:.4}, total_press={:.2}, thresh={:.2}", "python pzt angle: seq={} dts_ms={} angle={:.2} magnitude={:.2} state={} cop=({:.2},{:.2}) base=({:.2},{:.2}) total_press={:.2} threshold={:.2}",
message.seq,
message.dts_ms,
message.angle, message.angle,
message.magnitude, message.magnitude,
message.state, message.state,

View File

@@ -1,4 +1,3 @@
pub mod ad_solver;
mod commands; mod commands;
mod lan_game; mod lan_game;
pub mod log; pub mod log;
@@ -155,7 +154,8 @@ pub fn run() {
commands::devkit::devkit_stop, commands::devkit::devkit_stop,
commands::devkit::devkit_get_config, commands::devkit::devkit_get_config,
commands::devkit::devkit_set_config, commands::devkit::devkit_set_config,
commands::devkit::devkit_process_export commands::devkit::devkit_process_export,
commands::devkit::devkit_push_replay_frame
]); ]);
#[cfg(not(feature = "devkit"))] #[cfg(not(feature = "devkit"))]

View File

@@ -3,7 +3,7 @@ use fern::{
Dispatch, Dispatch,
}; };
use log::debug; use log::debug;
use std::{path::{Path, PathBuf}, time::SystemTime}; use std::{path::PathBuf, time::SystemTime};
fn log_directory() -> PathBuf { fn log_directory() -> PathBuf {
let base_dir = std::env::var_os("LOCALAPPDATA") let base_dir = std::env::var_os("LOCALAPPDATA")
@@ -67,6 +67,7 @@ pub fn setup_logger() {
Dispatch::new() Dispatch::new()
.level(log::LevelFilter::Debug) .level(log::LevelFilter::Debug)
.level_for("h2", log::LevelFilter::Info)
.chain(console_config) .chain(console_config)
.chain(file_config) .chain(file_config)
.apply() .apply()

View File

@@ -1,390 +0,0 @@
use crate::serial_core::codec::Codec;
use crate::serial_core::error::CodecError;
use crate::serial_core::frame::{
HandGatewayDataRepFrame, HandGatewayFrame, HandGatewayFrameMetaData, HandGatewayFrameNode,
};
use crate::serial_core::utils::{calc_crc8_itu, elapsed_millis};
use log::debug;
use std::time::Instant;
const FRAME_HEADER: [u8; 2] = [0x55, 0xAA];
const RESPONSE_DATA_CMD: u8 = 0x81;
const RESPONSE_DATA_FIXED_LENGTH: usize = 16;
const RESPONSE_DATA_PREFIX_LENGTH: usize = 19;
const MIN_FRAME_LENGTH: usize = 7;
const DEFAULT_MAX_FRAME_LENGTH: usize = 64 * 1024;
/// Describes one fixed MCU data block.
///
/// `config_mask` identifies the bit associated with this block in the protocol's
/// Config/ValidConfig fields. `sample_count` is the number of little-endian u16
/// samples in the block.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandGatewayNodeConfig {
pub config_mask: u32,
pub sample_count: usize,
}
impl HandGatewayNodeConfig {
pub const fn new(config_mask: u32, sample_count: usize) -> Self {
Self {
config_mask,
sample_count,
}
}
fn payload_len(&self, bytes_per_sample: usize) -> Option<usize> {
self.sample_count.checked_mul(bytes_per_sample)
}
}
/// Host-side description of the configuration currently hard-coded in the MCU.
///
/// Keep the node order identical to the MCU Config bit order. When the MCU gains
/// GET_CONFIG support, construct this value from that response instead of changing
/// the decoder.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandGatewayConfig {
pub protocol_version: u8,
pub bytes_per_sample: usize,
pub max_frame_length: usize,
pub nodes: Vec<HandGatewayNodeConfig>,
}
impl HandGatewayConfig {
pub fn new(nodes: Vec<HandGatewayNodeConfig>) -> Self {
Self {
protocol_version: 0x01,
bytes_per_sample: 2,
max_frame_length: DEFAULT_MAX_FRAME_LENGTH,
nodes,
}
}
fn validate(&self) -> Result<(), CodecError> {
if self.bytes_per_sample == 0 || self.max_frame_length < MIN_FRAME_LENGTH {
return Err(CodecError::InvalidLength);
}
let mut used_masks = 0u32;
for node in &self.nodes {
if node.config_mask == 0
|| node.config_mask.count_ones() != 1
|| used_masks & node.config_mask != 0
|| node.payload_len(self.bytes_per_sample).is_none()
{
return Err(CodecError::InvalidLength);
}
used_masks |= node.config_mask;
}
Ok(())
}
}
pub struct HandGatewayCodec {
buffer: Vec<u8>,
config: HandGatewayConfig,
}
impl HandGatewayCodec {
/// Convenience constructor for sequential Config bits (bit 0, bit 1, ...).
/// Each entry is the u16 sample count of one MCU node.
pub fn new(node_sample_counts: &[u16]) -> Self {
let nodes = node_sample_counts
.iter()
.enumerate()
.map(|(index, &sample_count)| {
let config_mask = 1u32.checked_shl(index as u32).unwrap_or(0);
HandGatewayNodeConfig::new(config_mask, usize::from(sample_count))
})
.collect();
Self {
buffer: Vec::new(),
config: HandGatewayConfig::new(nodes),
}
}
pub fn with_config(config: HandGatewayConfig) -> Result<Self, CodecError> {
config.validate()?;
Ok(Self {
buffer: Vec::new(),
config,
})
}
pub fn config(&self) -> &HandGatewayConfig {
&self.config
}
pub fn parse_node_payload(data: &[u8]) -> Result<Vec<u16>, CodecError> {
if data.len() % 2 != 0 {
return Err(CodecError::InvalidLength);
}
Ok(data
.chunks_exact(2)
.map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
.collect())
}
fn retain_possible_header_prefix(&mut self) {
if self.buffer.last() == Some(&FRAME_HEADER[0]) {
self.buffer.drain(..self.buffer.len() - 1);
} else {
self.buffer.clear();
}
}
fn parse_data_response(
&self,
frame: &[u8],
length: u16,
session_started_at: Instant,
) -> Result<HandGatewayFrame, CodecError> {
let payload_len = usize::from(length)
.checked_sub(RESPONSE_DATA_FIXED_LENGTH)
.ok_or(CodecError::InvalidLength)?;
let payload_end = RESPONSE_DATA_PREFIX_LENGTH
.checked_add(payload_len)
.ok_or(CodecError::PayloadTooLarge)?;
if payload_end + 1 != frame.len() {
return Err(CodecError::InvalidLength);
}
let version = frame[5];
if version != self.config.protocol_version {
return Err(CodecError::InvalidFrameType);
}
let timestamp = u32::from_le_bytes(frame[6..10].try_into().unwrap());
let config = u32::from_le_bytes(frame[10..14].try_into().unwrap());
let valid_config = u32::from_le_bytes(frame[14..18].try_into().unwrap());
let block_count = frame[18];
let active_nodes: Vec<&HandGatewayNodeConfig> = self
.config
.nodes
.iter()
.filter(|node| config & node.config_mask != 0)
.collect();
if active_nodes.len() != usize::from(block_count) {
return Err(CodecError::InvalidLength);
}
let expected_payload_len = active_nodes.iter().try_fold(0usize, |total, node| {
let node_len = node
.payload_len(self.config.bytes_per_sample)
.ok_or(CodecError::PayloadTooLarge)?;
total
.checked_add(node_len)
.ok_or(CodecError::PayloadTooLarge)
})?;
if expected_payload_len != payload_len {
return Err(CodecError::InvalidLength);
}
let mut cursor = RESPONSE_DATA_PREFIX_LENGTH;
let mut nodes = Vec::with_capacity(active_nodes.len());
for node in active_nodes {
let node_len = node
.payload_len(self.config.bytes_per_sample)
.ok_or(CodecError::PayloadTooLarge)?;
let next = cursor + node_len;
nodes.push(HandGatewayFrameNode {
config_mask: node.config_mask,
valid: valid_config & node.config_mask != 0,
payload: frame[cursor..next].to_vec(),
});
cursor = next;
}
Ok(HandGatewayFrame::DataRep(HandGatewayDataRepFrame {
meta: HandGatewayFrameMetaData {
header: FRAME_HEADER,
length,
cmd: RESPONSE_DATA_CMD,
version,
checksum: frame[frame.len() - 1],
},
timestamp,
config,
valid_config,
block_count,
nodes,
dts_ms: elapsed_millis(session_started_at),
}))
}
}
impl Codec<HandGatewayFrame> for HandGatewayCodec {
fn decode(
&mut self,
input: &[u8],
session_started_at: Instant,
) -> Result<Vec<HandGatewayFrame>, CodecError> {
self.config.validate()?;
self.buffer.extend_from_slice(input);
let mut frames = Vec::new();
loop {
let Some(header_pos) = self
.buffer
.windows(FRAME_HEADER.len())
.position(|window| window == FRAME_HEADER)
else {
self.retain_possible_header_prefix();
break;
};
if header_pos > 0 {
self.buffer.drain(..header_pos);
}
if self.buffer.len() < 4 {
break;
}
let length = u16::from_le_bytes([self.buffer[2], self.buffer[3]]);
let frame_len = usize::from(length)
.checked_add(4)
.ok_or(CodecError::PayloadTooLarge)?;
if frame_len < MIN_FRAME_LENGTH || frame_len > self.config.max_frame_length {
debug!("invalid hand gateway frame length: {frame_len}");
self.buffer.drain(..1);
continue;
}
if self.buffer.len() < frame_len {
break;
}
let expected_checksum = calc_crc8_itu(&self.buffer[..frame_len - 1]);
let received_checksum = self.buffer[frame_len - 1];
if expected_checksum != received_checksum {
debug!(
"hand gateway checksum mismatch: expected {expected_checksum:02X}, got {received_checksum:02X}"
);
self.buffer.drain(..1);
continue;
}
if self.buffer[4] == RESPONSE_DATA_CMD {
match self.parse_data_response(
&self.buffer[..frame_len],
length,
session_started_at,
) {
Ok(frame) => frames.push(frame),
Err(error) => debug!("invalid hand gateway data response: {error}"),
}
}
self.buffer.drain(..frame_len);
}
Ok(frames)
}
fn encode(&self, _frame: &HandGatewayFrame) -> Result<Vec<u8>, CodecError> {
Err(CodecError::InvalidFrameType)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn test_codec() -> HandGatewayCodec {
HandGatewayCodec::with_config(HandGatewayConfig::new(vec![
HandGatewayNodeConfig::new(1 << 0, 2),
HandGatewayNodeConfig::new(1 << 1, 1),
]))
.unwrap()
}
fn data_response(config: u32, valid_config: u32, payload: &[u8]) -> Vec<u8> {
let block_count = config.count_ones() as u8;
let length = (RESPONSE_DATA_FIXED_LENGTH + payload.len()) as u16;
let mut frame = Vec::new();
frame.extend_from_slice(&FRAME_HEADER);
frame.extend_from_slice(&length.to_le_bytes());
frame.push(RESPONSE_DATA_CMD);
frame.push(0x01);
frame.extend_from_slice(&123_456u32.to_le_bytes());
frame.extend_from_slice(&config.to_le_bytes());
frame.extend_from_slice(&valid_config.to_le_bytes());
frame.push(block_count);
frame.extend_from_slice(payload);
frame.push(calc_crc8_itu(&frame));
frame
}
#[test]
fn decodes_configured_nodes() {
let mut codec = test_codec();
let bytes = data_response(0b11, 0b01, &[1, 0, 2, 0, 3, 0]);
let frames = codec.decode(&bytes, Instant::now()).unwrap();
let HandGatewayFrame::DataRep(frame) = &frames[0];
assert_eq!(frame.timestamp, 123_456);
assert_eq!(frame.block_count, 2);
assert_eq!(frame.nodes.len(), 2);
assert_eq!(frame.nodes[0].config_mask, 1);
assert!(frame.nodes[0].valid);
assert_eq!(
HandGatewayCodec::parse_node_payload(&frame.nodes[0].payload).unwrap(),
[1, 2]
);
assert!(!frame.nodes[1].valid);
assert_eq!(
HandGatewayCodec::parse_node_payload(&frame.nodes[1].payload).unwrap(),
[3]
);
}
#[test]
fn supports_split_and_sticky_packets() {
let mut codec = test_codec();
let frame = data_response(0b01, 0b01, &[1, 0, 2, 0]);
let split = frame.len() / 2;
assert!(codec
.decode(&frame[..split], Instant::now())
.unwrap()
.is_empty());
let mut remainder_and_next = frame[split..].to_vec();
remainder_and_next.extend_from_slice(&frame);
let frames = codec.decode(&remainder_and_next, Instant::now()).unwrap();
assert_eq!(frames.len(), 2);
}
#[test]
fn resynchronizes_after_noise_and_bad_crc() {
let mut codec = test_codec();
let valid = data_response(0b10, 0b10, &[9, 0]);
let mut bad = valid.clone();
let last = bad.len() - 1;
bad[last] ^= 0xFF;
let mut bytes = vec![0x13, 0x55, 0x12];
bytes.extend_from_slice(&bad);
bytes.extend_from_slice(&valid);
let frames = codec.decode(&bytes, Instant::now()).unwrap();
assert_eq!(frames.len(), 1);
}
#[test]
fn rejects_payload_that_does_not_match_fixed_config() {
let mut codec = test_codec();
let bytes = data_response(0b01, 0b01, &[1, 0]);
assert!(codec.decode(&bytes, Instant::now()).unwrap().is_empty());
}
}

View File

@@ -2,5 +2,4 @@ use crate::serial_core::{frame::TestFrame, record::Recording};
pub mod test; pub mod test;
pub mod tactile_a; pub mod tactile_a;
pub mod hand_gateway;
pub type TestRecording = Recording<TestFrame>; pub type TestRecording = Recording<TestFrame>;

View File

@@ -14,7 +14,7 @@ use anyhow::anyhow;
use std::io::Read; use std::io::Read;
use log::debug; use log::debug;
const FINGER_FRAME_BUFFER_MIN_LENGTH: usize = 15; const FRAME_BUFFER_MIN_LENGTH: usize = 15;
pub struct TactileACodec { pub struct TactileACodec {
buffer: Vec<u8>, buffer: Vec<u8>,
@@ -77,12 +77,11 @@ impl TactileACodec {
.chunks_exact(2) .chunks_exact(2)
.map(|chunk| { .map(|chunk| {
let raw = u16::from_le_bytes([chunk[0], chunk[1]]) as i32; let raw = u16::from_le_bytes([chunk[0], chunk[1]]) as i32;
raw if raw < 15 {
// if raw < 15 { 0
// 0 } else {
// } else { raw
// raw }
// }
}) })
.collect::<Vec<i32>>(); .collect::<Vec<i32>>();
@@ -123,7 +122,7 @@ impl Codec<TactileAFrame> for TactileACodec {
let mut frames: Vec<TactileAFrame> = Vec::new(); let mut frames: Vec<TactileAFrame> = Vec::new();
loop { loop {
if self.buffer.len() < FINGER_FRAME_BUFFER_MIN_LENGTH { if self.buffer.len() < FRAME_BUFFER_MIN_LENGTH {
break; break;
} }
@@ -137,7 +136,7 @@ impl Codec<TactileAFrame> for TactileACodec {
self.buffer.drain(0..pos); self.buffer.drain(0..pos);
} }
if self.buffer.len() < FINGER_FRAME_BUFFER_MIN_LENGTH { if self.buffer.len() < FRAME_BUFFER_MIN_LENGTH {
break; break;
} }
@@ -165,7 +164,7 @@ impl Codec<TactileAFrame> for TactileACodec {
continue; continue;
} }
let frame_length = except_data_len + FINGER_FRAME_BUFFER_MIN_LENGTH; let frame_length = except_data_len + FRAME_BUFFER_MIN_LENGTH;
if self.buffer.len() < frame_length { if self.buffer.len() < frame_length {
break; break;
} }

View File

@@ -62,7 +62,6 @@ impl Codec<TestFrame> for TestCodec {
continue; continue;
} }
let dts = elapsed_millis(session_started_at); let dts = elapsed_millis(session_started_at);
println!("dts_ms: {dts}");
frames.push(TestFrame { frames.push(TestFrame {
header: [0xAA, 0x55], header: [0xAA, 0x55],
cmd: cmd, cmd: cmd,
@@ -244,11 +243,10 @@ mod tests {
fn test_read_csv_basic() -> anyhow::Result<()> { fn test_read_csv_basic() -> anyhow::Result<()> {
let mut rdr = Reader::from_path("recording_20260329_125238.csv")?; let mut rdr = Reader::from_path("recording_20260329_125238.csv")?;
let headers = rdr.headers()?; let headers = rdr.headers()?;
println!("headers: {:?}", headers);
for result in rdr.records() { for result in rdr.records() {
let record = result?; let record = result?;
println!("record: {:?}", record); let _ = record;
} }
Ok(()) Ok(())

View File

@@ -7,7 +7,7 @@ pub struct TestFrame {
pub length: usize, pub length: usize,
pub payload: Vec<u8>, pub payload: Vec<u8>,
pub checksum: u8, pub checksum: u8,
pub dts_ms: u64, pub dts_ms: u64
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@@ -35,54 +35,23 @@ pub struct TactileARepFrame {
pub meta: TactileAFrameMetaData, pub meta: TactileAFrameMetaData,
pub status: TactileAFrameStatusCode, pub status: TactileAFrameStatusCode,
pub payload: Vec<u8>, pub payload: Vec<u8>,
pub dts_ms: u64, pub dts_ms: u64
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TactileAFrameStatusCode { pub enum TactileAFrameStatusCode {
Success, Success,
Failure, Failure
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
pub enum TactileAFrame { pub enum TactileAFrame {
Req(TactileAReqFrame), Req(TactileAReqFrame),
Rep(TactileARepFrame), Rep(TactileARepFrame)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandGatewayFrameMetaData {
pub header: [u8; 2],
pub length: u16,
pub cmd: u8,
pub version: u8,
pub checksum: u8,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandGatewayFrameNode {
pub config_mask: u32,
pub valid: bool,
pub payload: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HandGatewayDataRepFrame {
pub meta: HandGatewayFrameMetaData,
pub timestamp: u32,
pub config: u32,
pub valid_config: u32,
pub block_count: u8,
pub nodes: Vec<HandGatewayFrameNode>,
pub dts_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum HandGatewayFrame {
DataRep(HandGatewayDataRepFrame),
} }
#[async_trait] #[async_trait]
pub trait FrameHandler<F, T>: Send { pub trait FrameHandler<F, T>: Send {
async fn on_frame(&mut self, frame: &F) -> Result<Option<Vec<T>>>; async fn on_frame(&mut self, frame: &F) -> Result<Option<Vec<T>>>;
} }

View File

@@ -118,6 +118,10 @@ impl HudChartState {
push_summary_point(&mut self.summary_points, value); push_summary_point(&mut self.summary_points, value);
} }
pub fn clear_summary(&mut self) {
self.summary_points.clear();
}
pub fn record_pressure_matrix(&mut self, values: &[i32]) { pub fn record_pressure_matrix(&mut self, values: &[i32]) {
if values.is_empty() { if values.is_empty() {
return; return;

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,6 @@
use crate::ad_solver::solve_for_x;
#[cfg(feature = "devkit")] #[cfg(feature = "devkit")]
use crate::devkit::{proto::SensorFrame, DevKitState}; use crate::devkit::{proto::SensorFrame, DevKitState};
use crate::serial_core::codec::Codec; use crate::serial_core::codec::Codec;
use crate::serial_core::codecs::hand_gateway::HandGatewayCodec;
use crate::serial_core::codecs::tactile_a::TactileACodec; use crate::serial_core::codecs::tactile_a::TactileACodec;
use crate::serial_core::frame::{FrameHandler, TactileAFrame, TestFrame}; use crate::serial_core::frame::{FrameHandler, TactileAFrame, TestFrame};
use crate::serial_core::model::{HudChartState, HudPacket, HudSpatialForce}; use crate::serial_core::model::{HudChartState, HudPacket, HudSpatialForce};
@@ -11,8 +9,6 @@ use crate::serial_core::multi_dim_force::PztProcessor;
use crate::serial_core::record::Recording; use crate::serial_core::record::Recording;
use crate::serial_core::record::{FrameTiming, RecordedFrame}; use crate::serial_core::record::{FrameTiming, RecordedFrame};
use anyhow::Result; use anyhow::Result;
use log::debug;
use log::info;
use std::future::pending; use std::future::pending;
#[cfg(feature = "devkit")] #[cfg(feature = "devkit")]
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -25,6 +21,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::{self, Duration, MissedTickBehavior}; use tokio::time::{self, Duration, MissedTickBehavior};
use tokio_serial::SerialStream; use tokio_serial::SerialStream;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
const AUTO_SUB_INTERVAL: Duration = Duration::from_nanos(16_666_667); const AUTO_SUB_INTERVAL: Duration = Duration::from_nanos(16_666_667);
pub enum PollMode<F> { pub enum PollMode<F> {
@@ -202,61 +199,6 @@ where
.await .await
} }
/// Runs the hand gateway in passive mode. The MCU continuously uploads data, so
/// this loop never writes request frames to the serial port.
pub async fn run_hand_gateway_serial(
app: AppHandle,
mut port: SerialStream,
mut codec: HandGatewayCodec,
session_started_at: Instant,
cancel: CancellationToken,
) -> Result<()> {
let mut buffer = [0u8; 4096];
loop {
tokio::select! {
_ = cancel.cancelled() => break,
read_result = port.read(&mut buffer) => {
let n = read_result?;
if n == 0 {
tokio::task::yield_now().await;
continue;
}
let raw = &buffer[..n];
app.emit("hand_gateway_raw", raw.to_vec())?;
let raw_hex = raw
.iter()
.map(|byte| format!("{byte:02X}"))
.collect::<Vec<_>>()
.join(" ");
info!("[hand_gateway][raw] {raw_hex}");
for frame in codec.decode(raw, session_started_at)? {
let crate::serial_core::frame::HandGatewayFrame::DataRep(frame) = frame;
info!(
"[hand_gateway][frame] timestamp_us={} config=0x{:08X} valid_config=0x{:08X} block_count={}",
frame.timestamp,
frame.config,
frame.valid_config,
frame.block_count,
);
for node in frame.nodes {
info!(
"[hand_gateway][node] mask=0x{:08X} valid={} payload={:02X?}",
node.config_mask,
node.valid,
node.payload,
);
}
}
}
}
}
Ok(())
}
pub async fn run_serial_with_poll<C, H, T, F>( pub async fn run_serial_with_poll<C, H, T, F>(
app: AppHandle, app: AppHandle,
mut port: SerialStream, mut port: SerialStream,
@@ -368,15 +310,14 @@ where
drop(record); drop(record);
if let Some(vals) = decode_res { if let Some(vals) = decode_res {
#[cfg(feature = "multi-dim")]
let mut spatial_force = None; let mut spatial_force = None;
#[cfg(not(feature = "multi-dim"))]
let spatial_force = None;
#[cfg(feature = "multi-dim")] #[cfg(feature = "multi-dim")]
{ {
let pzt_values = vals.iter().map(|value| *value as f32).collect::<Vec<f32>>(); let pzt_values = vals.iter().map(|value| *value as f32).collect::<Vec<f32>>();
if let Ok(analysis) = pzt_processor.get_pzt_analysis(&pzt_values) { if let Ok(analysis) = pzt_processor.get_pzt_analysis_at(&pzt_values, frame.dts_ms() as f32) {
// debug!(
// "spatial force: angle={:.2}°, magnitude={:.2}, dx={:.2}, dy={:.2}",
// analysis.angle_deg, analysis.magnitude, analysis.planar_x, analysis.planar_y
// );
if PztProcessor::should_report(&analysis) { if PztProcessor::should_report(&analysis) {
spatial_force = Some(HudSpatialForce { spatial_force = Some(HudSpatialForce {
angle_deg: analysis.angle_deg, angle_deg: analysis.angle_deg,
@@ -386,13 +327,15 @@ where
} }
} }
} }
#[cfg(feature = "devkit")] // #[cfg(feature = "devkit")]
{ // {
let summary = vals.iter().copied().sum::<i32>(); // let summary = vals.iter().copied().sum::<i32>();
let force = raw_to_g1(summary as u32); // #[cfg(feature = "debug")]
// let force = raw_to_g1(summary as u32);
push_devkit_frame(&app, vals.as_slice(), frame.dts_ms(), force); // #[cfg(not(feature = "debug"))]
} // let force = summary as f64;
// push_devkit_frame(&app, vals.as_slice(), frame.dts_ms(), force);
// }
pending_sub_frame = Some(PendingSubFrame { pending_sub_frame = Some(PendingSubFrame {
frame: frame.clone(), frame: frame.clone(),
@@ -415,15 +358,24 @@ fn build_display_values(
spatial_force: Option<HudSpatialForce>, spatial_force: Option<HudSpatialForce>,
) -> Option<Vec<i32>> { ) -> Option<Vec<i32>> {
let summary = values.iter().copied().sum::<i32>(); let summary = values.iter().copied().sum::<i32>();
let force = raw_to_g1(summary as u32);
// let force_solve = solve_for_x(summary as f64)?;
// println!("force_solve: {force_solve}");
chart_state.record_summary(force as f32);
chart_state.record_pressure_matrix(values);
chart_state.record_spatial_force(spatial_force); chart_state.record_spatial_force(spatial_force);
Some(vec![summary])
let x = raw_to_g1(summary as u32).min(MAX_DISPLAY_FORCE);
if x <= MIN_DISPLAY_FORCE {
let zero_values = vec![0; values.len()];
chart_state.record_summary(0.0);
chart_state.record_pressure_matrix(&zero_values);
return Some(vec![0]);
}
chart_state.record_pressure_matrix(values);
chart_state.record_summary(x as f32);
Some(vec![x.round() as i32])
} }
const MIN_DISPLAY_FORCE: f64 = 0.1;
const MAX_DISPLAY_FORCE: f64 = 25.6;
#[cfg(feature = "devkit")] #[cfg(feature = "devkit")]
fn push_devkit_frame(app: &AppHandle, values: &[i32], dts_ms: u64, resultant_force: f64) { fn push_devkit_frame(app: &AppHandle, values: &[i32], dts_ms: u64, resultant_force: f64) {
let devkit_state = app.state::<DevKitState>(); let devkit_state = app.state::<DevKitState>();
@@ -477,13 +429,12 @@ fn infer_matrix_shape(len: usize) -> (u32, u32) {
} }
fn raw_to_g1(raw: u32) -> f64 { fn raw_to_g1(raw: u32) -> f64 {
const X: [u32; 13] = [ const X: [u32; 12] = [
0, 16811, 41350, 79241, 94615, 127446, 149559, 175900, 195056, 237852, 267810, 322472, 0, 21382, 108507, 123183, 147405, 171105, 192395, 250443, 231423, 350560, 396616, 429444
378511,
]; ];
const Y: [f64; 13] = [ const Y: [f64; 12] = [
0.0, 57.0, 97.0, 197.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0, 0.0, 57.0, 257.0, 357.0, 457.0, 557.0, 657.0, 857.0, 1057.0, 1557.0, 2057.0, 2557.0
]; ];
let n = X.len(); let n = X.len();

View File

@@ -41,9 +41,7 @@
"template": "nsis/installer.nsi" "template": "nsis/installer.nsi"
} }
}, },
"resources": [ "resources": []
"resources/je-skin-devkit-server.exe"
]
}, },
"plugins": { "plugins": {
"updater": { "updater": {

View File

@@ -29,7 +29,6 @@
export let summary: HudSummary; export let summary: HudSummary;
export let pressureMatrix: number[] | null = null; export let pressureMatrix: number[] | null = null;
export let spatialForce: HudSpatialForce | null = null; export let spatialForce: HudSpatialForce | null = null;
export let devkitSpatialForce: HudSpatialForce | null = null;
export let showConfigPanel = false; export let showConfigPanel = false;
export let configPanelTitle = ""; export let configPanelTitle = "";
export let configPanelHint = ""; export let configPanelHint = "";
@@ -48,7 +47,7 @@
export let colorMapPreset: PressureColorMapPreset = "emerald"; export let colorMapPreset: PressureColorMapPreset = "emerald";
export let matrixDisplayMode: MatrixDisplayMode = "dots"; export let matrixDisplayMode: MatrixDisplayMode = "dots";
export let stageViewMode: StageViewMode = "webgl"; export let stageViewMode: StageViewMode = "webgl";
export let modelUrl = "/models/je-skin-model.gltf"; export let modelUrl = "/models/je-skin-model.glb";
export let replaySectionLabel = ""; export let replaySectionLabel = "";
export let replayPlayLabel = ""; export let replayPlayLabel = "";
export let replayPauseLabel = ""; export let replayPauseLabel = "";
@@ -61,8 +60,9 @@
export let replayProgress = 0; export let replayProgress = 0;
export let replayFileName = ""; export let replayFileName = "";
export let replayFrameInfo = ""; export let replayFrameInfo = "";
export let showPrecisionTestPanel = false;
export let sessionStartedAt: number = Date.now(); export let sessionStartedAt: number = Date.now();
export let summaryReleasePending = false;
export let spatialForcePanelVisible = false;
let stagePlaneEl: HTMLDivElement | undefined; let stagePlaneEl: HTMLDivElement | undefined;
let panelZoneEl: HTMLDivElement | undefined; let panelZoneEl: HTMLDivElement | undefined;
@@ -89,9 +89,9 @@
$: replaySide = summarySide === "left" ? "right" : "left"; $: replaySide = summarySide === "left" ? "right" : "left";
$: replayToggleButtonText = replayIsPlaying ? replayPauseLabel : replayPlayLabel; $: replayToggleButtonText = replayIsPlaying ? replayPauseLabel : replayPlayLabel;
$: replayProgressPercent = Math.round(Math.min(1, Math.max(0, replayProgress)) * 100); $: replayProgressPercent = Math.round(Math.min(1, Math.max(0, replayProgress)) * 100);
$: summaryCurveVisible = summary.points.length > 0 && summary.points.some((value) => Number.isFinite(value) && Math.abs(value) >= 0.0001); $: summaryCurveVisible =
$: splitMatrixTitle = locale === "zh-CN" ? "数字矩阵" : "Matrix"; summary.points.length > 0 &&
$: splitMatrixHint = locale === "zh-CN" ? "实时压力数据 / 数字矩阵" : "Live pressure matrix"; (summaryReleasePending || summary.points.some((value) => Number.isFinite(value) && Math.abs(value) >= 0.0001));
$: isModelStage = stageViewMode === "model3d"; $: isModelStage = stageViewMode === "model3d";
function toPxNumber(rawValue: string): number { function toPxNumber(rawValue: string): number {
@@ -117,7 +117,7 @@
return; return;
} }
panelZoneTopPx = showPrecisionTestPanel ? 24 : 16; panelZoneTopPx = 16;
const panelZoneBottomPx = panelZoneEl ? toPxNumber(getComputedStyle(panelZoneEl).bottom) : 0; const panelZoneBottomPx = panelZoneEl ? toPxNumber(getComputedStyle(panelZoneEl).bottom) : 0;
const zoneHeight = Math.max(0, stagePlaneEl.clientHeight - panelZoneTopPx - panelZoneBottomPx); const zoneHeight = Math.max(0, stagePlaneEl.clientHeight - panelZoneTopPx - panelZoneBottomPx);
@@ -191,43 +191,6 @@
<ModelStage {locale} {modelUrl} /> <ModelStage {locale} {modelUrl} />
{/key} {/key}
</div> </div>
{:else if showPrecisionTestPanel}
<div class="split-game-wrap">
<section class="split-panel split-matrix-panel">
<header class="split-panel-head">
<p>{splitMatrixTitle}</p>
<span>{splitMatrixHint}</span>
</header>
<div class="split-panel-body">
{#key `${matrixRows}x${matrixCols}:${colorMapPreset}:split`}
<PressureMatrixViewer
{summary}
{pressureMatrix}
{matrixRows}
{matrixCols}
{rangeMin}
{rangeMax}
{colorMapPreset}
{matrixDisplayMode}
{locale}
showStatsPanel={true}
/>
{/key}
</div>
</section>
<section class="split-panel split-breakout-panel">
<NeonBreakoutArena
{locale}
{pressureMatrix}
{matrixRows}
{matrixCols}
{rangeMin}
{rangeMax}
{colorMapPreset}
/>
</section>
</div>
{:else} {:else}
<div class="canvas-wrap"> <div class="canvas-wrap">
{#key `${matrixRows}x${matrixCols}:${colorMapPreset}`} {#key `${matrixRows}x${matrixCols}:${colorMapPreset}`}
@@ -247,7 +210,7 @@
</div> </div>
{/if} {/if}
{#if showConfigPanel && !showPrecisionTestPanel && !isModelStage} {#if showConfigPanel && !isModelStage}
<div class="config-panel-wrap"> <div class="config-panel-wrap">
<ConfigPanel <ConfigPanel
bind:matrixRows bind:matrixRows
@@ -269,7 +232,7 @@
</div> </div>
{/if} {/if}
{#if !showPrecisionTestPanel && !isModelStage} {#if !isModelStage}
<div class="panel-zone" bind:this={panelZoneEl}> <div class="panel-zone" bind:this={panelZoneEl}>
<aside class="side-rail left-rail"> <aside class="side-rail left-rail">
<div class="rail-stack" bind:this={leftStackEl}> <div class="rail-stack" bind:this={leftStackEl}>
@@ -317,42 +280,24 @@
<SignalChart {panel} panelIndex={index} {locale} /> <SignalChart {panel} panelIndex={index} {locale} />
</div> </div>
{/each} {/each}
{#if spatialForcePanelVisible}
<div <div
class="panel-motion-shell" class="panel-motion-shell"
in:fly={{ x: 180, duration: 340, opacity: 0.08, easing: cubicOut }} in:fly={{ x: 180, duration: 340, opacity: 0.08, easing: cubicOut }}
out:fly={{ x: 180, duration: 280, opacity: 0.06, easing: cubicIn }} out:fly={{ x: 180, duration: 280, opacity: 0.06, easing: cubicIn }}
> >
<SpatialForcePanel <SpatialForcePanel
{spatialForce} {spatialForce}
{locale} {locale}
side="right" side="right"
panelIndex={rightPanels.length} panelIndex={rightPanels.length}
panelCode="ALG" panelCode="3D"
panelTitle={locale === "zh-CN" ? "本地切向力" : "Local Tangential"} panelTitle={locale === "zh-CN" ? "三维力" : "3D Force"}
badgeLabel={locale === "zh-CN" ? "算法" : "ALGO"} badgeLabel=""
/> badgeTone="lime"
</div> />
</div>
<div {/if}
class="panel-motion-shell"
in:fly={{ x: 180, duration: 340, opacity: 0.08, easing: cubicOut }}
out:fly={{ x: 180, duration: 280, opacity: 0.06, easing: cubicIn }}
>
<SpatialForcePanel
spatialForce={devkitSpatialForce}
{locale}
side="right"
panelIndex={rightPanels.length + 1}
panelCode="DKT"
panelTitle={locale === "zh-CN" ? "DevKit 切向力" : "DevKit Tangential"}
badgeLabel="DEVKIT"
badgeTone="lime"
showMetrics={false}
requireMagnitude={false}
compactMetaText={locale === "zh-CN" ? "等待 DevKit 角度流" : "Waiting for DevKit angle"}
/>
</div>
{#if summaryCurveVisible && summarySide === "right"} {#if summaryCurveVisible && summarySide === "right"}
<div <div
@@ -368,7 +313,7 @@
{sessionStartedAt} {sessionStartedAt}
isRealtime={!replayHasData} isRealtime={!replayHasData}
side="right" side="right"
panelIndex={rightPanels.length} panelIndex={rightPanels.length + (spatialForcePanelVisible ? 1 : 0)}
/> />
</div> </div>
{/if} {/if}
@@ -377,7 +322,7 @@
</div> </div>
{/if} {/if}
{#if replayHasData && !showPrecisionTestPanel && !isModelStage} {#if replayHasData && !isModelStage}
<aside class="replay-floating-panel" class:is-left={replaySide === "left"} class:is-right={replaySide === "right"}> <aside class="replay-floating-panel" class:is-left={replaySide === "left"} class:is-right={replaySide === "right"}>
<div class="replay-panel-head"> <div class="replay-panel-head">
<div class="replay-panel-title-group"> <div class="replay-panel-title-group">
@@ -415,7 +360,7 @@
</aside> </aside>
{/if} {/if}
{#if !showPrecisionTestPanel && !isModelStage} {#if !isModelStage}
<div class="stage-bottom-overlay"> <div class="stage-bottom-overlay">
<slot /> <slot />
</div> </div>

View File

@@ -2,10 +2,8 @@
import { createEventDispatcher } from "svelte"; import { createEventDispatcher } from "svelte";
import type { import type {
ConnectionState, ConnectionState,
HudConfigLink,
HudNoticeTone, HudNoticeTone,
LocaleCode, LocaleCode,
MatrixDisplayMode,
StageViewMode, StageViewMode,
WindowControlAction WindowControlAction
} from "$lib/types/hud"; } from "$lib/types/hud";
@@ -29,15 +27,8 @@
export let serialPortValue = ""; export let serialPortValue = "";
export let serialPortOptions: string[] = []; export let serialPortOptions: string[] = [];
export let refreshPortsLabel = ""; export let refreshPortsLabel = "";
export let configLinksLabel = "";
export let configLinks: HudConfigLink[] = [];
export let matrixViewLabel = ""; export let matrixViewLabel = "";
export let matrixViewNumericLabel = "";
export let matrixViewDotsLabel = ""; export let matrixViewDotsLabel = "";
export let matrixDisplayMode: MatrixDisplayMode = "dots";
export let stageModeLabel = "";
export let stageModeWebglLabel = "";
export let stageModeModelLabel = "";
export let stageViewMode: StageViewMode = "webgl"; export let stageViewMode: StageViewMode = "webgl";
export let connectActionLabel = ""; export let connectActionLabel = "";
export let disconnectActionLabel = ""; export let disconnectActionLabel = "";
@@ -59,7 +50,6 @@
const dispatch = createEventDispatcher<{ const dispatch = createEventDispatcher<{
windowcontrol: WindowControlAction; windowcontrol: WindowControlAction;
localechange: LocaleCode; localechange: LocaleCode;
configlink: string;
matrixdisplaytoggle: boolean; matrixdisplaytoggle: boolean;
stagemodechange: StageViewMode; stagemodechange: StageViewMode;
portchange: string; portchange: string;
@@ -103,14 +93,6 @@
dispatch("localechange", nextLocale); dispatch("localechange", nextLocale);
} }
function emitConfigLink(linkId: string): void {
dispatch("configlink", linkId);
}
function emitMatrixDisplayToggle(): void {
dispatch("matrixdisplaytoggle", matrixDisplayMode !== "dots");
}
function emitStageModeChange(nextMode: StageViewMode): void { function emitStageModeChange(nextMode: StageViewMode): void {
dispatch("stagemodechange", nextMode); dispatch("stagemodechange", nextMode);
} }
@@ -195,60 +177,6 @@
<div class="control-bar"> <div class="control-bar">
<div class="control-main-row"> <div class="control-main-row">
<section class="config-links" aria-label={configLinksLabel}>
{#each configLinks as link (link.id)}
<button
type="button"
class="config-link tone-{link.tone ?? 'neutral'}"
class:is-active={Boolean(link.active)}
on:click={() => emitConfigLink(link.id)}
>
<span class="config-indicator" aria-hidden="true"></span>
<span class="config-label">{link.label}</span>
</button>
{/each}
</section>
<section class="matrix-switch-wrap" aria-label={matrixViewLabel}>
<span class="matrix-switch-label">{matrixViewLabel}</span>
<button
type="button"
class="matrix-switch-btn"
class:is-active={matrixDisplayMode === "dots"}
role="switch"
aria-checked={matrixDisplayMode === "dots"}
aria-label={matrixViewDotsLabel}
on:click={emitMatrixDisplayToggle}
>
<span class="matrix-switch-track" aria-hidden="true">
<span class="matrix-switch-thumb"></span>
</span>
<span class="matrix-switch-copy">{matrixDisplayMode === "dots" ? matrixViewDotsLabel : matrixViewNumericLabel}</span>
</button>
</section>
<section class="stage-mode-switch" aria-label={stageModeLabel}>
<span class="stage-mode-label">{stageModeLabel}</span>
<div class="stage-mode-options" role="group" aria-label={stageModeLabel}>
<button
type="button"
class="stage-mode-btn"
class:is-active={stageViewMode === "webgl"}
on:click={() => emitStageModeChange("webgl")}
>
{stageModeWebglLabel}
</button>
<button
type="button"
class="stage-mode-btn"
class:is-active={stageViewMode === "model3d"}
on:click={() => emitStageModeChange("model3d")}
>
{stageModeModelLabel}
</button>
</div>
</section>
<section class="state-card" aria-label={connectionLabel}> <section class="state-card" aria-label={connectionLabel}>
<span class="state-dot" class:ok={connectionTone === "ok"} class:warn={connectionTone === "warn"}></span> <span class="state-dot" class:ok={connectionTone === "ok"} class:warn={connectionTone === "warn"}></span>
<span class="state-label">{connectionLabel}</span> <span class="state-label">{connectionLabel}</span>
@@ -517,149 +445,6 @@
background: var(--panel-surface); background: var(--panel-surface);
} }
.matrix-switch-wrap,
.stage-mode-switch {
display: inline-flex;
align-items: center;
gap: 0.4rem;
min-block-size: 2rem;
border: 1px solid var(--panel-line);
border-radius: 999px;
padding: 0.16rem 0.22rem 0.16rem 0.56rem;
background: var(--panel-surface);
}
.matrix-switch-label,
.stage-mode-label {
color: var(--panel-text-dim);
font-size: 0.66rem;
letter-spacing: 0.08em;
text-transform: uppercase;
line-height: 1;
white-space: nowrap;
}
.matrix-switch-btn {
display: inline-flex;
align-items: center;
gap: 0.42rem;
min-block-size: 1.62rem;
border: 1px solid rgb(var(--hud-border-rgb) / 0.26);
border-radius: 999px;
padding: 0.18rem 0.28rem 0.18rem 0.22rem;
background: rgb(var(--hud-surface-deep-rgb) / 0.84);
color: rgb(var(--hud-text-main-rgb) / 0.92);
cursor: pointer;
transition:
border-color 180ms ease,
box-shadow 180ms ease,
background-color 180ms ease,
color 180ms ease;
}
.matrix-switch-btn:hover {
border-color: rgb(var(--hud-cyan-rgb) / 0.4);
}
.matrix-switch-btn.is-active {
border-color: rgb(var(--hud-cyan-rgb) / 0.5);
background:
linear-gradient(180deg, rgb(var(--hud-surface-alt-rgb) / 0.94), rgb(var(--hud-surface-rgb) / 0.9)),
radial-gradient(circle at 50% 0, rgb(var(--hud-cyan-rgb) / 0.12), transparent 60%);
box-shadow:
inset 0 0 0 1px rgb(var(--hud-text-main-rgb) / 0.05),
0 0 12px rgb(var(--hud-cyan-rgb) / 0.12);
}
.matrix-switch-track {
position: relative;
display: inline-flex;
align-items: center;
inline-size: 2.2rem;
block-size: 1.2rem;
border-radius: 999px;
padding: 0.14rem;
background: rgb(var(--hud-surface-rgb) / 0.9);
box-shadow: inset 0 0 0 1px rgb(var(--hud-border-rgb) / 0.24);
transition:
background-color 180ms ease,
box-shadow 180ms ease;
}
.matrix-switch-btn.is-active .matrix-switch-track {
background: rgb(var(--hud-cyan-rgb) / 0.18);
box-shadow: inset 0 0 0 1px rgb(var(--hud-cyan-rgb) / 0.18);
}
.matrix-switch-thumb {
inline-size: 0.92rem;
block-size: 0.92rem;
border-radius: 50%;
background: rgb(var(--hud-text-main-rgb) / 0.96);
box-shadow:
0 1px 4px rgb(0 0 0 / 0.26),
0 0 10px rgb(var(--hud-text-main-rgb) / 0.12);
transform: translateX(0);
transition:
transform 180ms ease,
background-color 180ms ease,
box-shadow 180ms ease;
}
.matrix-switch-btn.is-active .matrix-switch-thumb {
transform: translateX(0.96rem);
background: rgb(var(--hud-cyan-rgb) / 0.96);
box-shadow:
0 1px 4px rgb(0 0 0 / 0.26),
0 0 12px rgb(var(--hud-cyan-rgb) / 0.22);
}
.matrix-switch-copy {
font-size: 0.74rem;
letter-spacing: 0.04em;
white-space: nowrap;
line-height: 1;
}
.stage-mode-options {
display: inline-flex;
align-items: center;
gap: 0.18rem;
padding: 0.16rem;
border: 1px solid rgb(var(--hud-border-rgb) / 0.24);
border-radius: 999px;
background: rgb(var(--hud-surface-deep-rgb) / 0.8);
}
.stage-mode-btn {
min-block-size: 1.38rem;
border: 1px solid transparent;
border-radius: 999px;
padding: 0.18rem 0.54rem;
background: transparent;
color: rgb(var(--hud-text-dim-rgb) / 0.88);
font: inherit;
font-size: 0.7rem;
letter-spacing: 0.04em;
cursor: pointer;
transition:
border-color 180ms ease,
background-color 180ms ease,
color 180ms ease,
box-shadow 180ms ease;
}
.stage-mode-btn:hover {
color: rgb(var(--hud-text-main-rgb) / 0.96);
}
.stage-mode-btn.is-active {
border-color: rgb(var(--hud-cyan-rgb) / 0.42);
background: rgb(var(--hud-cyan-rgb) / 0.14);
color: rgb(var(--hud-text-main-rgb) / 0.98);
box-shadow: 0 0 12px rgb(var(--hud-cyan-rgb) / 0.1);
}
.state-dot { .state-dot {
inline-size: 0.55rem; inline-size: 0.55rem;
block-size: 0.55rem; block-size: 0.55rem;
@@ -1123,93 +908,6 @@
background: rgb(10 16 20 / 0.7); background: rgb(10 16 20 / 0.7);
} }
.config-links {
display: inline-flex;
align-items: center;
gap: 0.2rem;
min-block-size: 2rem;
border: 1px solid rgb(95 132 158 / 0.36);
border-radius: 999px;
padding: 0.17rem 0.2rem;
background: linear-gradient(180deg, rgb(9 15 19 / 0.9), rgb(4 8 12 / 0.86));
box-shadow: inset 0 0 0 1px rgb(140 184 210 / 0.06);
}
.config-link {
display: inline-flex;
align-items: center;
gap: 0.34rem;
border: 1px solid transparent;
border-radius: 999px;
padding: 0.26rem 0.64rem;
background: transparent;
color: rgb(164 188 208 / 0.9);
font-size: 0.81rem;
letter-spacing: 0.05em;
cursor: pointer;
transition:
color 180ms ease,
border-color 180ms ease,
background-color 180ms ease,
box-shadow 220ms ease;
}
.config-indicator {
inline-size: 0.34rem;
block-size: 0.34rem;
border-radius: 999px;
background: rgb(136 157 174 / 0.88);
box-shadow: 0 0 0 2px rgb(136 157 174 / 0.16);
transition:
background-color 180ms ease,
box-shadow 200ms ease;
}
.config-label {
line-height: 1;
}
.config-link:hover {
color: #d7edfb;
border-color: rgb(62 232 255 / 0.26);
}
.config-link.is-active {
color: #f1fdff;
border-color: rgb(106 150 180 / 0.56);
background: rgb(18 27 35 / 0.9);
box-shadow:
inset 0 0 0 1px rgb(167 218 252 / 0.08),
0 0 10px rgb(62 232 255 / 0.12);
}
.config-link.tone-cyan.is-active {
border-color: rgb(62 232 255 / 0.48);
}
.config-link.tone-lime.is-active {
border-color: rgb(133 255 68 / 0.52);
}
.config-link.tone-orange.is-active {
border-color: rgb(255 91 63 / 0.52);
}
.config-link.tone-cyan.is-active .config-indicator {
background: var(--hud-cyan);
box-shadow: 0 0 0 2px rgb(62 232 255 / 0.17);
}
.config-link.tone-lime.is-active .config-indicator {
background: var(--hud-lime);
box-shadow: 0 0 0 2px rgb(133 255 68 / 0.17);
}
.config-link.tone-orange.is-active .config-indicator {
background: var(--hud-orange);
box-shadow: 0 0 0 2px rgb(255 91 63 / 0.18);
}
.locale-btn { .locale-btn {
border: 1px solid transparent; border: 1px solid transparent;
border-radius: 999px; border-radius: 999px;
@@ -1236,21 +934,11 @@
background: rgb(24 31 25 / 0.9); background: rgb(24 31 25 / 0.9);
} }
@media (max-width: 1080px) {
.config-links {
flex-wrap: wrap;
}
}
@media (max-width: 820px) { @media (max-width: 820px) {
.control-main-row { .control-main-row {
gap: 0.44rem; gap: 0.44rem;
} }
.config-links {
flex-wrap: wrap;
}
.serial-select { .serial-select {
padding-inline-start: 0.45rem; padding-inline-start: 0.45rem;
} }

View File

@@ -9,7 +9,7 @@
type ModelLoadState = "loading" | "ready" | "missing" | "error"; type ModelLoadState = "loading" | "ready" | "missing" | "error";
export let locale: LocaleCode = "zh-CN"; export let locale: LocaleCode = "zh-CN";
export let modelUrl = "/models/je-skin-model.glb"; export let modelUrl = "/models/je-skin-model.gltf";
let rootEl: HTMLDivElement | undefined; let rootEl: HTMLDivElement | undefined;
let canvasEl: HTMLCanvasElement | undefined; let canvasEl: HTMLCanvasElement | undefined;
@@ -19,15 +19,43 @@
const FLOOR_Y = -1.15; const FLOOR_Y = -1.15;
const MODEL_FLOOR_CLEARANCE = 0.035; const MODEL_FLOOR_CLEARANCE = 0.035;
const MODEL_TARGET_HEIGHT = 6.55; const MODEL_TARGET_HEIGHT = 8.4;
const MODEL_MIN_SCALE = 0.02; const MODEL_MIN_SCALE = 0.02;
const MODEL_MAX_SCALE = 80; const MODEL_MAX_SCALE = 80;
const FIXED_CAMERA_POSITION = new THREE.Vector3(-0.113, -0.149, 10.911); const CAMERA_DISTANCE_FACTOR = 1.35;
const FIXED_CAMERA_TARGET = new THREE.Vector3(0, 2.35, 0); const CAMERA_DISTANCE_MIN = 7.5;
const MODEL_FRONT_ROTATION_Y = -Math.PI / 2; const CAMERA_DISTANCE_MAX = 24;
const MODEL_UPRIGHT_ROTATION_Z = Math.PI * 1.5 - THREE.MathUtils.degToRad(2.2);
$: canvasLabel = locale === "zh-CN" ? "3D 模型" : "3D Model"; $: copy =
locale === "zh-CN"
? {
title: "3D 模型舱",
subtitle: "Dark Grid / Future Lab",
loading: "正在加载模型",
ready: "模型已载入",
missing: "等待模型文件",
error: "模型加载失败",
modelPath: "模型路径",
hint: "请使用 glTF 2.0 的 .glb/.gltf旧版 glTF 1.0 需要先转换"
}
: {
title: "3D Model Bay",
subtitle: "Dark Grid / Future Lab",
loading: "Loading model",
ready: "Model loaded",
missing: "Waiting for model file",
error: "Model load failed",
modelPath: "Model path",
hint: "Use glTF 2.0 .glb/.gltf assets; older glTF 1.0 files need conversion first"
};
$: statusText =
loadState === "ready"
? copy.ready
: loadState === "missing"
? copy.missing
: loadState === "error"
? copy.error
: `${copy.loading} ${Math.round(loadProgress)}%`;
function disposeObject3D(object: THREE.Object3D): void { function disposeObject3D(object: THREE.Object3D): void {
object.traverse((child) => { object.traverse((child) => {
@@ -100,41 +128,7 @@
return Math.min(max, Math.max(min, value)); return Math.min(max, Math.max(min, value));
} }
function materialToUnlit(material: THREE.Material): THREE.Material {
const source = material as THREE.MeshStandardMaterial & THREE.MeshPhysicalMaterial;
const unlit = new THREE.MeshBasicMaterial({
color: source.color?.clone() ?? new THREE.Color(0xffffff),
map: source.map ?? null,
transparent: source.transparent,
opacity: source.opacity,
alphaMap: source.alphaMap ?? null,
side: source.side,
depthWrite: source.depthWrite,
depthTest: source.depthTest,
vertexColors: source.vertexColors,
toneMapped: false
});
return unlit;
}
function applyUnlitMaterials(object: THREE.Object3D): void {
object.traverse((child) => {
const mesh = child as THREE.Mesh;
if (!mesh.isMesh || !mesh.material) {
return;
}
mesh.castShadow = false;
mesh.receiveShadow = false;
mesh.material = Array.isArray(mesh.material)
? mesh.material.map(materialToUnlit)
: materialToUnlit(mesh.material);
});
}
function normalizeObjectToStage(object: THREE.Object3D): THREE.Box3 { function normalizeObjectToStage(object: THREE.Object3D): THREE.Box3 {
object.rotation.set(0, MODEL_FRONT_ROTATION_Y, MODEL_UPRIGHT_ROTATION_Z);
object.updateMatrixWorld(true); object.updateMatrixWorld(true);
let bounds = new THREE.Box3().setFromObject(object); let bounds = new THREE.Box3().setFromObject(object);
const size = bounds.getSize(new THREE.Vector3()); const size = bounds.getSize(new THREE.Vector3());
@@ -155,14 +149,19 @@
} }
function frameObject(object: THREE.Object3D, camera: THREE.PerspectiveCamera, controls: OrbitControls): void { function frameObject(object: THREE.Object3D, camera: THREE.PerspectiveCamera, controls: OrbitControls): void {
normalizeObjectToStage(object); const bounds = normalizeObjectToStage(object);
camera.position.copy(FIXED_CAMERA_POSITION); const size = bounds.getSize(new THREE.Vector3());
camera.near = 0.05; const maxAxis = Math.max(size.x, size.y, size.z, 1);
camera.far = 600; const distance = clamp(maxAxis * CAMERA_DISTANCE_FACTOR, CAMERA_DISTANCE_MIN, CAMERA_DISTANCE_MAX);
const targetY = FLOOR_Y + Math.max(size.y * 0.46, 1.4);
camera.position.set(distance * 0.48, targetY + distance * 0.24, distance * 0.68);
camera.near = Math.max(distance / 80, 0.01);
camera.far = distance * 24;
camera.updateProjectionMatrix(); camera.updateProjectionMatrix();
controls.target.copy(FIXED_CAMERA_TARGET); controls.target.set(0, targetY, 0);
controls.minDistance = 2.4; controls.minDistance = Math.max(distance * 0.32, 2);
controls.maxDistance = 32; controls.maxDistance = Math.max(distance * 2.5, 12);
controls.update(); controls.update();
} }
@@ -187,15 +186,14 @@
scene.fog = new THREE.FogExp2(0x03070d, 0.028); scene.fog = new THREE.FogExp2(0x03070d, 0.028);
const camera = new THREE.PerspectiveCamera(38, 1, 0.05, 600); const camera = new THREE.PerspectiveCamera(38, 1, 0.05, 600);
camera.position.copy(FIXED_CAMERA_POSITION); camera.position.set(8, 6, 9);
camera.lookAt(FIXED_CAMERA_TARGET);
const controls = new OrbitControls(camera, canvasEl); const controls = new OrbitControls(camera, canvasEl);
controls.enableDamping = true; controls.enableDamping = true;
controls.dampingFactor = 0.08; controls.dampingFactor = 0.08;
controls.minDistance = 2.4; controls.minDistance = 2.4;
controls.maxDistance = 32; controls.maxDistance = 32;
controls.target.copy(FIXED_CAMERA_TARGET); controls.target.set(0, FLOOR_Y + 3.2, 0);
const labGroup = new THREE.Group(); const labGroup = new THREE.Group();
scene.add(labGroup); scene.add(labGroup);
@@ -243,8 +241,16 @@
floor.position.y = FLOOR_Y - 0.018; floor.position.y = FLOOR_Y - 0.018;
labGroup.add(floor); labGroup.add(floor);
const ambient = new THREE.AmbientLight(0x9fb8d0, 0.22);
const keyLight = new THREE.DirectionalLight(0x7be7ff, 1.5);
keyLight.position.set(8, 12, 8);
const rimLight = new THREE.PointLight(0xa6ff7a, 26, 24, 2.1);
rimLight.position.set(-4.5, 4.8, -3.6);
const sideLight = new THREE.PointLight(0x5c8cff, 15, 28, 1.7);
sideLight.position.set(5.8, 3.2, -5.4);
scene.add(ambient, keyLight, rimLight, sideLight);
let activeModel: THREE.Object3D = buildPlaceholderModel(); let activeModel: THREE.Object3D = buildPlaceholderModel();
applyUnlitMaterials(activeModel);
scene.add(activeModel); scene.add(activeModel);
frameObject(activeModel, camera, controls); frameObject(activeModel, camera, controls);
@@ -255,7 +261,13 @@
scene.remove(activeModel); scene.remove(activeModel);
disposeObject3D(activeModel); disposeObject3D(activeModel);
activeModel = gltf.scene; activeModel = gltf.scene;
applyUnlitMaterials(activeModel); activeModel.traverse((child) => {
const mesh = child as THREE.Mesh;
if (mesh.isMesh) {
mesh.castShadow = true;
mesh.receiveShadow = true;
}
});
scene.add(activeModel); scene.add(activeModel);
frameObject(activeModel, camera, controls); frameObject(activeModel, camera, controls);
loadState = "ready"; loadState = "ready";
@@ -318,9 +330,20 @@
</script> </script>
<div class="model-stage" bind:this={rootEl}> <div class="model-stage" bind:this={rootEl}>
<canvas class="model-canvas" bind:this={canvasEl} aria-label={canvasLabel}></canvas> <canvas class="model-canvas" bind:this={canvasEl} aria-label={copy.title}></canvas>
<div class="model-vignette" aria-hidden="true"></div> <div class="model-vignette" aria-hidden="true"></div>
<div class="model-scanlines" aria-hidden="true"></div> <div class="model-scanlines" aria-hidden="true"></div>
<section class="model-hud" aria-label={copy.title}>
<p class="model-kicker">{copy.subtitle}</p>
<h2>{copy.title}</h2>
<div class="model-status-row">
<span class="status-light" class:is-ready={loadState === "ready"}></span>
<span>{statusText}</span>
</div>
<p class="model-path">{copy.modelPath}: {modelUrl}</p>
<p class="model-hint">{loadError || copy.hint}</p>
</section>
</div> </div>
<style> <style>
@@ -365,4 +388,82 @@
mix-blend-mode: screen; mix-blend-mode: screen;
} }
.model-hud {
position: absolute;
top: clamp(1.2rem, 2.8vw, 2.2rem);
left: clamp(1.2rem, 2.8vw, 2.4rem);
z-index: 2;
display: grid;
gap: 0.42rem;
max-inline-size: min(22rem, 42vw);
padding: 0.9rem 1rem 1rem;
border: 1px solid rgb(94 231 255 / 0.24);
border-radius: 0.7rem;
background:
linear-gradient(180deg, rgb(8 18 28 / 0.82), rgb(3 9 15 / 0.72)),
radial-gradient(circle at 0 0, rgb(94 231 255 / 0.1), transparent 44%);
box-shadow:
inset 0 1px 0 rgb(255 255 255 / 0.06),
0 0 28px rgb(94 231 255 / 0.08);
backdrop-filter: blur(10px);
}
.model-kicker,
.model-path,
.model-hint {
margin: 0;
color: rgb(198 226 239 / 0.72);
font-size: 0.6rem;
letter-spacing: 0.1em;
text-transform: uppercase;
line-height: 1.35;
}
h2 {
margin: 0;
color: rgb(241 251 255 / 0.96);
font-size: clamp(1.15rem, 1.1vw + 0.88rem, 1.72rem);
line-height: 1.05;
font-weight: 650;
}
.model-status-row {
display: inline-flex;
align-items: center;
gap: 0.44rem;
color: rgb(229 249 255 / 0.94);
font-size: 0.78rem;
letter-spacing: 0.04em;
}
.status-light {
inline-size: 0.58rem;
block-size: 0.58rem;
border-radius: 50%;
background: rgb(255 188 92 / 0.95);
box-shadow: 0 0 0 2px rgb(255 188 92 / 0.16), 0 0 12px rgb(255 188 92 / 0.18);
}
.status-light.is-ready {
background: rgb(166 255 122 / 0.95);
box-shadow: 0 0 0 2px rgb(166 255 122 / 0.16), 0 0 14px rgb(166 255 122 / 0.22);
}
.model-path {
color: rgb(94 231 255 / 0.78);
text-transform: none;
word-break: break-word;
}
.model-hint {
color: rgb(198 226 239 / 0.66);
text-transform: none;
letter-spacing: 0.04em;
}
@media (max-width: 960px) {
.model-hud {
max-inline-size: min(20rem, calc(100% - 2.4rem));
}
}
</style> </style>

View File

@@ -67,6 +67,7 @@
const CAMERA_TARGET_Z = MATRIX_OFFSET_Z - 0.4; const CAMERA_TARGET_Z = MATRIX_OFFSET_Z - 0.4;
const MATRIX_ROTATION_Y = 0; const MATRIX_ROTATION_Y = 0;
const rangeStopPositions = [0, 0.25, 0.5, 0.75, 0.875, 1] as const; const rangeStopPositions = [0, 0.25, 0.5, 0.75, 0.875, 1] as const;
const maxDisplayForce = 25.6;
const labelVector = new THREE.Vector3(); const labelVector = new THREE.Vector3();
$: resolvedColorPalette = pressureColorPalettes[colorMapPreset] ?? pressureColorPalettes.emerald; $: resolvedColorPalette = pressureColorPalettes[colorMapPreset] ?? pressureColorPalettes.emerald;
@@ -145,6 +146,10 @@
return "--"; return "--";
} }
if (value >= maxDisplayForce) {
return `${maxDisplayForce.toFixed(1)}+`;
}
return value.toFixed(1); return value.toFixed(1);
} }
@@ -672,14 +677,6 @@
<span class="stats-key">{viewerI18n.current}</span> <span class="stats-key">{viewerI18n.current}</span>
<strong class="stats-value">{formatForceStat(stats.current)}</strong> <strong class="stats-value">{formatForceStat(stats.current)}</strong>
</article> </article>
<article class="stats-card">
<span class="stats-key">{viewerI18n.max}</span>
<strong class="stats-value">{formatForceStat(stats.max)}</strong>
</article>
<article class="stats-card">
<span class="stats-key">{viewerI18n.min}</span>
<strong class="stats-value">{formatForceStat(stats.min)}</strong>
</article>
</div> </div>
<p class="stats-note">{statsNote}</p> <p class="stats-note">{statsNote}</p>
</section> </section>
@@ -768,7 +765,7 @@
.stats-grid { .stats-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: minmax(0, 1fr);
gap: 0.46rem; gap: 0.46rem;
} }

View File

@@ -11,6 +11,7 @@
const viewportWidth = 100; const viewportWidth = 100;
const viewportHeight = 36; const viewportHeight = 36;
const maxDisplayForce = 25.6;
const toneColorMap: Record<SignalTone, string> = { const toneColorMap: Record<SignalTone, string> = {
cyan: "62 232 255", cyan: "62 232 255",
@@ -32,7 +33,15 @@
} }
function formatMetric(value: number | null): string { function formatMetric(value: number | null): string {
return value === null ? "--" : value.toFixed(1); if (value === null) {
return "--";
}
if (value >= maxDisplayForce) {
return `${maxDisplayForce.toFixed(1)}+`;
}
return value.toFixed(1);
} }
function resolveBounds(seriesCollection: HudSignalPanel["series"]): { min: number; max: number } { function resolveBounds(seriesCollection: HudSignalPanel["series"]): { min: number; max: number } {

View File

@@ -9,17 +9,7 @@
export let panelTitle = ""; export let panelTitle = "";
export let badgeLabel = ""; export let badgeLabel = "";
export let badgeTone: "cyan" | "lime" | "orange" = "cyan"; export let badgeTone: "cyan" | "lime" | "orange" = "cyan";
export let showMetrics = true;
export let requireMagnitude = true; export let requireMagnitude = true;
export let compactMetaText = "";
function formatValue(value: number | null, digits = 1): string {
if (value === null || !Number.isFinite(value)) {
return "--";
}
return value.toFixed(digits);
}
function normalizeAngle(value: number): number { function normalizeAngle(value: number): number {
return ((value % 360) + 360) % 360; return ((value % 360) + 360) % 360;
@@ -30,6 +20,7 @@
return delta === -180 ? 180 : delta; return delta === -180 ? 180 : delta;
} }
const displayAngleOffsetDeg = 90;
const jumpAngleThresholdDeg = 72; const jumpAngleThresholdDeg = 72;
let visualAngleDeg = 0; let visualAngleDeg = 0;
@@ -83,7 +74,7 @@
$: i18n = $: i18n =
locale === "zh-CN" locale === "zh-CN"
? { ? {
title: "切向力方向", title: "三维力",
waiting: "等待数据", waiting: "等待数据",
angle: "ANGLE", angle: "ANGLE",
heading: "方向角", heading: "方向角",
@@ -91,7 +82,7 @@
confidence: "置信度" confidence: "置信度"
} }
: { : {
title: "Tangential Direction", title: "3D Force",
waiting: "Waiting", waiting: "Waiting",
angle: "ANGLE", angle: "ANGLE",
heading: "Heading", heading: "Heading",
@@ -100,23 +91,19 @@
}; };
$: resolvedTitle = panelTitle || i18n.title; $: resolvedTitle = panelTitle || i18n.title;
$: resolvedBadgeLabel = badgeLabel || i18n.angle; $: resolvedBadgeLabel = badgeLabel || i18n.angle;
$: resolvedCompactMetaText =
compactMetaText || (locale === "zh-CN" ? "仅使用角度流" : "Angle stream only");
$: hasData = $: hasData =
spatialForce !== null && spatialForce !== null &&
Number.isFinite(spatialForce.angleDeg) && Number.isFinite(spatialForce.angleDeg) &&
(!requireMagnitude || Number.isFinite(spatialForce.magnitude)); (!requireMagnitude || (Number.isFinite(spatialForce.magnitude) && Math.abs(spatialForce.magnitude) >= 0.0001));
$: angleDeg = hasData ? normalizeAngle(spatialForce?.angleDeg ?? 0) : 0; $: angleDeg = hasData ? normalizeAngle((spatialForce?.angleDeg ?? 0) + displayAngleOffsetDeg) : 0;
$: updateVisualAngle(angleDeg, hasData); $: updateVisualAngle(angleDeg, hasData);
$: magnitude = hasData ? spatialForce?.magnitude ?? 0 : null;
$: confidence = hasData ? (spatialForce?.confidence ?? 0) * 100 : null;
</script> </script>
<article <article
class="signal-panel spatial-panel side-{side}" class="signal-panel spatial-panel side-{side}"
class:is-empty={!hasData} class:is-empty={!hasData}
aria-hidden={false} aria-label={resolvedTitle}
style="--panel-index: {panelIndex};" style="--panel-index: {panelIndex};"
> >
<header class="panel-head"> <header class="panel-head">
@@ -125,9 +112,11 @@
<p class="panel-title">{resolvedTitle}</p> <p class="panel-title">{resolvedTitle}</p>
</div> </div>
<div class="icon-layer" aria-hidden="true"> {#if resolvedBadgeLabel}
<span class={`icon-chip tone-${badgeTone}`}>{resolvedBadgeLabel}</span> <div class="icon-layer" aria-hidden="true">
</div> <span class={`icon-chip tone-${badgeTone}`}>{resolvedBadgeLabel}</span>
</div>
{/if}
</header> </header>
<div class="panel-body"> <div class="panel-body">
@@ -148,42 +137,28 @@
</div> </div>
{/if} {/if}
<div class="compass-center"></div> <div class="compass-center"></div>
<span class="compass-label label-top">90</span> <span class="compass-label label-top">0</span>
<span class="compass-label label-right">0</span> <span class="compass-label label-right">270</span>
<span class="compass-label label-bottom">270</span> <span class="compass-label label-bottom">180</span>
<span class="compass-label label-left">180</span> <span class="compass-label label-left">90</span>
</div> </div>
{#if !hasData}
<div class="empty-state">
<span>{i18n.waiting}</span>
</div>
{/if}
</div> </div>
<div class="angle-stage">
<p class="angle-label">{i18n.heading}</p>
{#if showMetrics}
<p class="angle-meta">{i18n.strength}: {formatValue(magnitude, 2)}</p>
<p class="angle-meta">{i18n.confidence}: {hasData ? `${formatValue(confidence, 0)}%` : "--"}</p>
{:else}
<p class="angle-meta">{resolvedCompactMetaText}</p>
<p class="angle-meta">{hasData ? (locale === "zh-CN" ? "实时对比中" : "Live comparison") : "--"}</p>
{/if}
</div>
</div> </div>
</article> </article>
<style> <style>
.signal-panel { .signal-panel {
--offset-x: 12%; --offset-x: 12%;
--enter-ms: 1800ms; --enter-ms: 1800ms;
--fade-ms: 1000ms; --fade-ms: 1000ms;
overflow: hidden; overflow: hidden;
inline-size: min(100%, clamp(34rem, 44vw, 44rem)); inline-size: var(--rail-width, min(100%, clamp(34rem, 44vw, 44rem)));
max-inline-size: 100%;
box-sizing: border-box;
flex: 0 0 var(--rail-width, auto);
justify-self: start; justify-self: start;
display: grid; display: grid;
grid-template-rows: auto 1fr; grid-template-rows: auto 1fr auto;
gap: 0.68rem; gap: 0.68rem;
padding: 0.88rem 0.96rem 1rem; padding: 0.88rem 0.96rem 1rem;
border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.42); border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.42);
@@ -218,6 +193,16 @@
opacity: 0.82; opacity: 0.82;
} }
.spatial-panel::after {
content: "";
display: block;
block-size: 1rem;
}
.spatial-panel {
margin-block-end: clamp(0.8rem, 1.8vh, 1.4rem);
}
.panel-head { .panel-head {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@@ -277,16 +262,18 @@
.panel-body { .panel-body {
display: grid; display: grid;
grid-template-columns: minmax(0, 1.1fr) minmax(10rem, 0.9fr); grid-template-columns: minmax(0, 1fr);
gap: 0.72rem; gap: 0.72rem;
block-size: clamp(12rem, 15.5vw, 15rem); block-size: clamp(14rem, 17vw, 16.5rem);
min-block-size: clamp(12rem, 15.5vw, 15rem); min-block-size: 5rem;
} }
.compass-stage { .compass-stage {
position: relative; position: relative;
min-block-size: 0; min-block-size: 0;
box-sizing: border-box;
overflow: hidden; overflow: hidden;
padding: 1.35rem 1.7rem;
border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.32); border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.32);
border-radius: 0.62rem; border-radius: 0.62rem;
background: background:
@@ -298,7 +285,8 @@
.compass-core { .compass-core {
position: relative; position: relative;
inline-size: min(72%, 13rem); inline-size: min(72%, 12.2rem, calc(100% - 3.4rem));
min-inline-size: 6.2rem;
aspect-ratio: 1; aspect-ratio: 1;
} }
@@ -397,26 +385,26 @@
} }
.label-top { .label-top {
top: -0.9rem; top: 0.35rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
.label-right { .label-right {
top: 50%; top: 50%;
right: -1rem; right: 0.42rem;
transform: translateY(-50%); transform: translateY(-50%);
} }
.label-bottom { .label-bottom {
bottom: -0.9rem; bottom: 0.35rem;
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
} }
.label-left { .label-left {
top: 50%; top: 50%;
left: -1.35rem; left: 0.42rem;
transform: translateY(-50%); transform: translateY(-50%);
} }
@@ -433,76 +421,79 @@
background: linear-gradient(180deg, rgb(var(--hud-surface-deep-rgb) / 0.06), rgb(var(--hud-surface-deep-rgb) / 0.18)); background: linear-gradient(180deg, rgb(var(--hud-surface-deep-rgb) / 0.06), rgb(var(--hud-surface-deep-rgb) / 0.18));
} }
.angle-stage {
border: 1px solid rgb(var(--hud-border-rgb) / 0.26);
border-radius: 0.62rem;
padding: 0.9rem 0.85rem;
block-size: 100%;
min-block-size: 0;
overflow: hidden;
background:
linear-gradient(180deg, rgb(var(--hud-surface-rgb) / 0.72), rgb(var(--hud-surface-deep-rgb) / 0.84)),
radial-gradient(circle at 50% 0, rgb(var(--hud-glow-rgb) / 0.05), transparent 58%);
display: grid;
grid-template-rows: auto auto auto;
align-content: center;
justify-items: start;
gap: 0.36rem;
}
.angle-label {
margin: 0;
color: rgb(var(--hud-text-dim-rgb) / 0.82);
font-size: 0.68rem;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.angle-meta {
margin: 0;
inline-size: 10rem;
min-block-size: 1rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: rgb(var(--hud-text-dim-rgb) / 0.84);
font-size: 0.68rem;
font-variant-numeric: tabular-nums;
letter-spacing: 0.05em;
}
@media (max-width: 1180px) { @media (max-width: 1180px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(28rem, 40vw, 38rem)); inline-size: var(--rail-width, min(100%, clamp(28rem, 40vw, 38rem)));
}
.panel-body {
block-size: clamp(12rem, 14.5vw, 14rem);
}
.compass-core {
inline-size: min(64%, 10.4rem, calc(100% - 3.4rem));
} }
} }
@media (max-height: 900px) { @media (max-height: 900px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(28rem, 38vw, 36rem)); inline-size: var(--rail-width, min(100%, clamp(28rem, 38vw, 36rem)));
padding: 0.7rem 0.76rem 0.8rem; padding: 0.7rem 0.76rem 0.8rem;
} }
.panel-body {
block-size: clamp(11.2rem, 13.5vw, 13.2rem);
}
.compass-stage {
padding-block: 1.2rem;
}
.compass-core {
inline-size: min(62%, 10rem, calc(100% - 3.4rem));
}
} }
@media (max-height: 760px) { @media (max-height: 760px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(24rem, 34vw, 30rem)); inline-size: var(--rail-width, min(100%, clamp(24rem, 34vw, 30rem)));
padding: 0.62rem 0.68rem 0.72rem; padding: 0.62rem 0.68rem 0.72rem;
gap: 0.48rem; gap: 0.48rem;
} }
.panel-body { .panel-body {
block-size: clamp(9rem, 10vw, 10.8rem); block-size: clamp(9.4rem, 11vw, 10.8rem);
min-block-size: clamp(9rem, 10vw, 10.8rem); }
.compass-stage {
padding: 1rem 1.45rem;
}
.compass-core {
inline-size: min(58%, 8.2rem, calc(100% - 2.9rem));
min-inline-size: 5.6rem;
} }
} }
@media (max-height: 680px) { @media (max-height: 680px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(20rem, 28vw, 26rem)); inline-size: var(--rail-width, min(100%, clamp(20rem, 28vw, 26rem)));
padding: 0.52rem 0.58rem 0.6rem; padding: 0.52rem 0.58rem 0.6rem;
gap: 0.36rem; gap: 0.36rem;
} }
.panel-body {
block-size: clamp(7.6rem, 9vw, 8.8rem);
}
.compass-stage {
padding: 0.82rem 1.25rem;
}
.compass-core {
inline-size: min(54%, 6.4rem, calc(100% - 2.5rem));
min-inline-size: 4.8rem;
}
} }
@media (max-width: 900px) { @media (max-width: 900px) {

View File

@@ -11,15 +11,26 @@
export let sessionStartedAt: number = Date.now(); export let sessionStartedAt: number = Date.now();
export let isRealtime = false; export let isRealtime = false;
let canvasEl: HTMLCanvasElement | undefined;
let currentTimeSeconds = 0; let currentTimeSeconds = 0;
let timerId: ReturnType<typeof setInterval> | null = null; let timerId: ReturnType<typeof setInterval> | null = null;
let resizeObserver: ResizeObserver | null = null;
onMount(() => { onMount(() => {
timerId = setInterval(() => { timerId = setInterval(() => {
currentTimeSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10; currentTimeSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10;
}, 200); }, 200);
if (canvasEl) {
resizeObserver = new ResizeObserver(() => {
drawCanvas(canvasEl, plotPoints, yAxisTicks, xAxisTicks, sampleCount);
});
resizeObserver.observe(canvasEl);
}
return () => { return () => {
if (timerId != null) clearInterval(timerId); if (timerId != null) clearInterval(timerId);
resizeObserver?.disconnect();
}; };
}); });
@@ -33,7 +44,8 @@
const plotInsetRight = 4; const plotInsetRight = 4;
const plotInsetTop = 4; const plotInsetTop = 4;
const plotInsetBottom = 9; const plotInsetBottom = 9;
const fixedYBounds = { min: 0, max: 25 }; const maxDisplayForce = 25.6;
const fixedYBounds = { min: 0, max: maxDisplayForce };
interface CurveSample { interface CurveSample {
x: number; x: number;
@@ -56,14 +68,6 @@
return Math.min(max, Math.max(min, value)); return Math.min(max, Math.max(min, value));
} }
function formatValue(value: number | null): string {
if (value === null) {
return "--";
}
return value.toFixed(1);
}
function formatAxisValue(value: number, axis: "x" | "y"): string { function formatAxisValue(value: number, axis: "x" | "y"): string {
if (!Number.isFinite(value)) { if (!Number.isFinite(value)) {
return "--"; return "--";
@@ -225,24 +229,117 @@
})); }));
} }
function createLinePath(points: PlotPoint[]): string { function drawCanvas(
if (points.length === 0) { canvas: HTMLCanvasElement | undefined,
return ""; points: PlotPoint[],
yTicks: AxisTick[],
xTicks: AxisTick[],
count: number
): void {
if (!canvas) {
return;
} }
return points.map((point, index) => `${index === 0 ? "M" : "L"} ${point.x} ${point.y}`).join(" "); const context = canvas.getContext("2d");
} if (!context) {
return;
function createAreaPath(points: PlotPoint[]): string {
if (points.length < 2) {
return "";
} }
const linePath = createLinePath(points); const cssWidth = Math.max(1, canvas.clientWidth);
const firstPoint = points[0]; const cssHeight = Math.max(1, canvas.clientHeight);
const lastPoint = points[points.length - 1]; const dpr = typeof window === "undefined" ? 1 : window.devicePixelRatio || 1;
const targetWidth = Math.round(cssWidth * dpr);
const targetHeight = Math.round(cssHeight * dpr);
return `${linePath} L ${lastPoint.x} ${viewportHeight - plotInsetBottom} L ${firstPoint.x} ${viewportHeight - plotInsetBottom} Z`; if (canvas.width !== targetWidth || canvas.height !== targetHeight) {
canvas.width = targetWidth;
canvas.height = targetHeight;
}
context.setTransform(targetWidth / viewportWidth, 0, 0, targetHeight / viewportHeight, 0, 0);
context.clearRect(0, 0, viewportWidth, viewportHeight);
context.save();
context.lineWidth = 0.45;
context.strokeStyle = "rgb(120 180 150 / 0.16)";
for (const tick of yTicks) {
context.beginPath();
context.moveTo(plotInsetLeft, tick.plotY);
context.lineTo(viewportWidth - plotInsetRight, tick.plotY);
context.stroke();
}
context.restore();
if (points.length >= 2) {
const baselineY = viewportHeight - plotInsetBottom;
const fill = context.createLinearGradient(0, plotInsetTop, 0, baselineY);
fill.addColorStop(0, "rgb(62 232 255 / 0.28)");
fill.addColorStop(1, "rgb(62 232 255 / 0.02)");
context.save();
context.beginPath();
context.moveTo(points[0].x, points[0].y);
for (let index = 1; index < points.length; index += 1) {
context.lineTo(points[index].x, points[index].y);
}
context.lineTo(points[points.length - 1].x, baselineY);
context.lineTo(points[0].x, baselineY);
context.closePath();
context.fillStyle = fill;
context.fill();
context.restore();
}
if (points.length > 0) {
context.save();
context.beginPath();
context.moveTo(points[0].x, points[0].y);
for (let index = 1; index < points.length; index += 1) {
context.lineTo(points[index].x, points[index].y);
}
context.lineWidth = 1.35;
context.lineCap = "round";
context.lineJoin = "round";
context.strokeStyle = "rgb(130 232 255 / 0.96)";
context.shadowColor = "rgb(62 232 255 / 0.22)";
context.shadowBlur = 4;
context.stroke();
context.restore();
const lastPoint = points[points.length - 1];
context.save();
context.beginPath();
context.arc(lastPoint.x, lastPoint.y, 1.7, 0, Math.PI * 2);
context.fillStyle = "rgb(73 222 128 / 0.98)";
context.shadowColor = "rgb(73 222 128 / 0.3)";
context.shadowBlur = 6;
context.fill();
context.restore();
}
context.save();
context.font = "600 3.2px sans-serif";
context.textBaseline = "middle";
context.shadowColor = "rgb(0 0 0 / 0.3)";
context.shadowBlur = 4;
context.fillStyle = "rgb(145 185 165 / 0.84)";
for (const tick of yTicks) {
context.textAlign = "right";
context.fillText(tick.label, tick.plotX, tick.plotY + 0.5);
}
context.fillStyle = "rgb(180 220 200 / 0.9)";
context.textBaseline = "alphabetic";
for (let index = 0; index < xTicks.length; index += 1) {
const tick = xTicks[index];
context.textAlign = index === 0 ? "left" : index === xTicks.length - 1 ? "right" : "center";
context.fillText(tick.label, tick.plotX, tick.plotY);
}
context.restore();
if (count === 0) {
context.clearRect(0, 0, viewportWidth, viewportHeight);
}
} }
$: sourceYValues = yValues && yValues.length ? yValues : summary.points; $: sourceYValues = yValues && yValues.length ? yValues : summary.points;
@@ -261,14 +358,9 @@
$: xDataBounds = resolveDataBounds(samples.map((sample) => sample.x)); $: xDataBounds = resolveDataBounds(samples.map((sample) => sample.x));
$: yDataBounds = resolveDataBounds(samples.map((sample) => sample.y)); $: yDataBounds = resolveDataBounds(samples.map((sample) => sample.y));
$: plotPoints = convertPoints(samples, xScaleBounds, yScaleBounds); $: plotPoints = convertPoints(samples, xScaleBounds, yScaleBounds);
$: linePath = createLinePath(plotPoints);
$: areaPath = createAreaPath(plotPoints);
$: lastPoint = plotPoints.length > 0 ? plotPoints[plotPoints.length - 1] : null;
$: yAxisTicks = sampleCount > 0 ? buildYAxisTicks(yScaleBounds, yDataBounds) : []; $: yAxisTicks = sampleCount > 0 ? buildYAxisTicks(yScaleBounds, yDataBounds) : [];
$: xAxisTicks = sampleCount > 0 ? buildXAxisTicks(xScaleBounds) : []; $: xAxisTicks = sampleCount > 0 ? buildXAxisTicks(xScaleBounds) : [];
$: latestValue = formatValue(summary.latest); $: drawCanvas(canvasEl, plotPoints, yAxisTicks, xAxisTicks, sampleCount);
$: minValue = formatValue(summary.min);
$: maxValue = formatValue(summary.max);
</script> </script>
<article <article
@@ -280,62 +372,12 @@
<header class="panel-head"> <header class="panel-head">
<div class="head-text"> <div class="head-text">
<p class="panel-code">RF</p> <p class="panel-code">RF</p>
<p class="panel-title">{summary.label}</p> <p class="panel-title">{locale === "zh-CN" ? "合力" : "Resultant Force"}</p>
</div>
<div class="icon-layer" aria-hidden="true">
<span class="icon-chip tone-cyan">NOW</span>
<span class="icon-chip tone-lime">MIN</span>
<span class="icon-chip tone-orange">MAX</span>
</div> </div>
</header> </header>
<div class="chart-stage"> <div class="chart-stage">
<svg viewBox="0 0 {viewportWidth} {viewportHeight}" preserveAspectRatio="none" role="img" aria-label={summary.label}> <canvas bind:this={canvasEl} aria-label={summary.label}></canvas>
<defs>
<linearGradient id="summary-fill" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="rgb(62 232 255 / 0.28)" />
<stop offset="100%" stop-color="rgb(62 232 255 / 0.02)" />
</linearGradient>
</defs>
<g class="grid-lines" aria-hidden="true">
{#each yAxisTicks as tick (`grid-${tick.value}`)}
<line x1={plotInsetLeft} y1={tick.plotY} x2={viewportWidth - plotInsetRight} y2={tick.plotY}></line>
{/each}
</g>
{#if areaPath}
<path d={areaPath} class="summary-area"></path>
{/if}
{#if linePath}
<path d={linePath} class="summary-line"></path>
{/if}
{#if lastPoint}
<circle cx={lastPoint.x} cy={lastPoint.y} r="1.7" class="summary-dot"></circle>
{/if}
<g class="axis-labels" aria-hidden="true">
{#each yAxisTicks as tick, index (`y-${index}`)}
<text class="axis-label y-axis-label" x={tick.plotX} y={tick.plotY + 1.1} text-anchor="end">
{tick.label}
</text>
{/each}
{#each xAxisTicks as tick, index (`x-${index}`)}
<text
class="axis-label x-axis-label"
x={tick.plotX}
y={tick.plotY}
text-anchor={index === 0 ? "start" : index === xAxisTicks.length - 1 ? "end" : "middle"}
>
{tick.label}
</text>
{/each}
</g>
</svg>
{#if sampleCount === 0} {#if sampleCount === 0}
<div class="empty-state"> <div class="empty-state">
@@ -343,24 +385,6 @@
</div> </div>
{/if} {/if}
</div> </div>
<footer class="panel-foot">
<p class="foot-item">
<span class="dot tone-cyan"></span>
<span class="metric-text">{i18n.now}</span>
<span class="value">{latestValue}</span>
</p>
<p class="foot-item">
<span class="dot tone-lime"></span>
<span class="metric-text">{i18n.min}</span>
<span class="value">{minValue}</span>
</p>
<p class="foot-item">
<span class="dot tone-orange"></span>
<span class="metric-text">{i18n.max}</span>
<span class="value">{maxValue}</span>
</p>
</footer>
</article> </article>
<style> <style>
@@ -369,10 +393,13 @@
--enter-ms: 1800ms; --enter-ms: 1800ms;
--fade-ms: 1000ms; --fade-ms: 1000ms;
overflow: hidden; overflow: hidden;
inline-size: min(100%, clamp(34rem, 44vw, 44rem)); inline-size: var(--rail-width, min(100%, clamp(34rem, 44vw, 44rem)));
max-inline-size: 100%;
box-sizing: border-box;
flex: 0 0 var(--rail-width, auto);
justify-self: start; justify-self: start;
display: grid; display: grid;
grid-template-rows: auto 1fr auto; grid-template-rows: auto 1fr;
gap: 0.68rem; gap: 0.68rem;
padding: 0.88rem 0.96rem 1rem; padding: 0.88rem 0.96rem 1rem;
border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.42); border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.42);
@@ -438,36 +465,6 @@
letter-spacing: 0.05em; letter-spacing: 0.05em;
} }
.icon-layer {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
gap: 0.26rem;
}
.icon-chip {
border: 1px solid rgb(var(--hud-border-strong-rgb) / 0.44);
border-radius: 999px;
padding: 0.08rem 0.36rem;
font-size: 0.58rem;
letter-spacing: 0.08em;
color: rgb(var(--hud-text-main-rgb) / 0.94);
background: rgb(var(--hud-surface-rgb) / 0.66);
}
.icon-chip.tone-cyan {
border-color: rgb(var(--hud-cyan-rgb) / 0.54);
}
.icon-chip.tone-lime {
border-color: rgb(var(--hud-lime-rgb) / 0.56);
}
.icon-chip.tone-orange {
border-color: rgb(var(--hud-orange-rgb) / 0.58);
}
.chart-stage { .chart-stage {
position: relative; position: relative;
block-size: clamp(12rem, 15.5vw, 15rem); block-size: clamp(12rem, 15.5vw, 15rem);
@@ -480,53 +477,12 @@
radial-gradient(circle at 50% 0, rgb(var(--hud-glow-rgb) / 0.09), transparent 45%); radial-gradient(circle at 50% 0, rgb(var(--hud-glow-rgb) / 0.09), transparent 45%);
} }
svg { canvas {
display: block; display: block;
inline-size: 100%; inline-size: 100%;
block-size: 100%; block-size: 100%;
} }
.grid-lines line {
stroke: rgb(var(--hud-border-strong-rgb) / 0.16);
stroke-width: 0.45;
}
.summary-area {
fill: url(#summary-fill);
}
.summary-line {
fill: none;
stroke: rgb(var(--hud-cyan-rgb) / 0.96);
stroke-width: 1.35;
stroke-linecap: round;
stroke-linejoin: round;
filter: drop-shadow(0 0 4px rgb(var(--hud-cyan-rgb) / 0.22));
}
.summary-dot {
fill: rgb(var(--hud-lime-rgb) / 0.98);
filter: drop-shadow(0 0 6px rgb(var(--hud-lime-rgb) / 0.3));
}
.axis-label {
fill: rgb(var(--hud-text-main-rgb) / 0.88);
font-size: 3.2px;
font-weight: 600;
letter-spacing: 0.02em;
text-shadow:
0 1px 0 rgb(0 0 0 / 0.46),
0 0 4px rgb(0 0 0 / 0.3);
}
.y-axis-label {
fill: rgb(var(--hud-text-dim-rgb) / 0.84);
}
.x-axis-label {
fill: rgb(var(--hud-text-dim-rgb) / 0.9);
}
.empty-state { .empty-state {
position: absolute; position: absolute;
inset: 0; inset: 0;
@@ -540,58 +496,9 @@
background: linear-gradient(180deg, rgb(var(--hud-surface-deep-rgb) / 0.06), rgb(var(--hud-surface-deep-rgb) / 0.18)); background: linear-gradient(180deg, rgb(var(--hud-surface-deep-rgb) / 0.06), rgb(var(--hud-surface-deep-rgb) / 0.18));
} }
.panel-foot {
display: flex;
flex-wrap: nowrap;
gap: 0.8rem;
margin: 0;
padding: 0;
flex-shrink: 0;
}
.foot-item {
margin: 0;
display: inline-flex;
align-items: center;
gap: 0.22rem;
color: rgb(var(--hud-text-main-rgb) / 0.9);
font-size: 0.68rem;
letter-spacing: 0.03em;
white-space: nowrap;
flex-shrink: 0;
}
.metric-text {
color: rgb(var(--hud-text-dim-rgb) / 0.82);
text-transform: uppercase;
flex-shrink: 0;
}
.dot {
inline-size: 0.34rem;
block-size: 0.34rem;
border-radius: 50%;
}
.dot.tone-cyan {
background: rgb(var(--hud-cyan-rgb));
}
.dot.tone-lime {
background: rgb(var(--hud-lime-rgb));
}
.dot.tone-orange {
background: rgb(var(--hud-orange-rgb));
}
.value {
min-inline-size: 2.6rem;
}
@media (max-width: 1180px) { @media (max-width: 1180px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(28rem, 40vw, 38rem)); inline-size: var(--rail-width, min(100%, clamp(28rem, 40vw, 38rem)));
} }
.chart-stage { .chart-stage {
@@ -601,7 +508,7 @@
@media (max-height: 900px) { @media (max-height: 900px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(28rem, 38vw, 36rem)); inline-size: var(--rail-width, min(100%, clamp(28rem, 38vw, 36rem)));
padding: 0.7rem 0.76rem 0.8rem; padding: 0.7rem 0.76rem 0.8rem;
} }
@@ -612,7 +519,7 @@
@media (max-height: 760px) { @media (max-height: 760px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(24rem, 34vw, 30rem)); inline-size: var(--rail-width, min(100%, clamp(24rem, 34vw, 30rem)));
padding: 0.62rem 0.68rem 0.72rem; padding: 0.62rem 0.68rem 0.72rem;
gap: 0.48rem; gap: 0.48rem;
} }
@@ -624,7 +531,7 @@
@media (max-height: 680px) { @media (max-height: 680px) {
.signal-panel { .signal-panel {
inline-size: min(100%, clamp(20rem, 28vw, 26rem)); inline-size: var(--rail-width, min(100%, clamp(20rem, 28vw, 26rem)));
padding: 0.52rem 0.58rem 0.6rem; padding: 0.52rem 0.58rem 0.6rem;
gap: 0.36rem; gap: 0.36rem;
} }

View File

@@ -14,8 +14,6 @@ export type MatrixDisplayMode = "numeric" | "dots";
export type SignalPanelSide = "left" | "right"; export type SignalPanelSide = "left" | "right";
export type HudConfigTone = "neutral" | "cyan" | "lime" | "orange";
export interface HudSignalSeries { export interface HudSignalSeries {
id: string; id: string;
tone: SignalTone; tone: SignalTone;
@@ -64,13 +62,6 @@ export interface HudSummary {
max: number | null; max: number | null;
} }
export interface HudConfigLink {
id: string;
label: string;
tone?: HudConfigTone;
active?: boolean;
}
export interface HudColorMapOption { export interface HudColorMapOption {
id: PressureColorMapPreset; id: PressureColorMapPreset;
label: string; label: string;
@@ -107,7 +98,6 @@ export interface HudCopy {
deviceLabel: string; deviceLabel: string;
sampleRateLabel: string; sampleRateLabel: string;
channelsLabel: string; channelsLabel: string;
configLinksLabel: string;
refreshPortsLabel: string; refreshPortsLabel: string;
connectActionLabel: string; connectActionLabel: string;
disconnectActionLabel: string; disconnectActionLabel: string;
@@ -169,6 +159,7 @@ export interface SerialRecordStateResult {
export interface SerialImportFrameResult { export interface SerialImportFrameResult {
data: number[]; data: number[];
dtsMs: number; dtsMs: number;
spatialForce: HudSpatialForce | null;
} }
export interface SerialImportResult { export interface SerialImportResult {

View File

@@ -19,7 +19,6 @@
FileExplorerRoot, FileExplorerRoot,
HudColorMapOption, HudColorMapOption,
HudCopy, HudCopy,
HudConfigLink,
HudNoticeTone, HudNoticeTone,
HudPacket, HudPacket,
HudSpatialForce, HudSpatialForce,
@@ -28,7 +27,6 @@
HudSignalSeries, HudSignalSeries,
HudSummary, HudSummary,
LocaleCode, LocaleCode,
MatrixDisplayMode,
SerialConnectResult, SerialConnectResult,
SerialExportResult, SerialExportResult,
SerialRecordStateResult, SerialRecordStateResult,
@@ -44,6 +42,7 @@
interface ReplayFrame { interface ReplayFrame {
values: number[]; values: number[];
dtsMs: number; dtsMs: number;
spatialForce?: HudSpatialForce | null;
} }
interface DevKitPztAngleEvent { interface DevKitPztAngleEvent {
@@ -51,6 +50,20 @@
timestampMs: number; timestampMs: number;
dtsMs: number; dtsMs: number;
angle: number; angle: number;
magnitude: number;
state: number;
copX: number;
copY: number;
baseX: number;
baseY: number;
totalPress: number;
threshold: number;
}
interface DevKitReplayFramePushResult {
seq: number;
timestampMs: number;
dtsMs: number;
} }
const copyByLocale: Record<LocaleCode, HudCopy> = { const copyByLocale: Record<LocaleCode, HudCopy> = {
@@ -69,7 +82,7 @@
rangeMaxLabel: "最大值", rangeMaxLabel: "最大值",
colorMapLabel: "映射颜色", colorMapLabel: "映射颜色",
matrixViewLabel: "矩阵模式", matrixViewLabel: "矩阵模式",
matrixViewNumericLabel: "数字矩阵", matrixViewNumericLabel: "数字模式",
matrixViewDotsLabel: "点矩阵", matrixViewDotsLabel: "点矩阵",
stageModeLabel: "渲染模式", stageModeLabel: "渲染模式",
stageModeWebglLabel: "WebGL", stageModeWebglLabel: "WebGL",
@@ -84,7 +97,6 @@
deviceLabel: "设备", deviceLabel: "设备",
sampleRateLabel: "采样率", sampleRateLabel: "采样率",
channelsLabel: "通道", channelsLabel: "通道",
configLinksLabel: "配置链接",
refreshPortsLabel: "刷新", refreshPortsLabel: "刷新",
connectActionLabel: "连接", connectActionLabel: "连接",
disconnectActionLabel: "断开", disconnectActionLabel: "断开",
@@ -146,7 +158,6 @@
deviceLabel: "Device", deviceLabel: "Device",
sampleRateLabel: "Sample Rate", sampleRateLabel: "Sample Rate",
channelsLabel: "Channels", channelsLabel: "Channels",
configLinksLabel: "Config Links",
refreshPortsLabel: "Refresh", refreshPortsLabel: "Refresh",
connectActionLabel: "Connect", connectActionLabel: "Connect",
disconnectActionLabel: "Disconnect", disconnectActionLabel: "Disconnect",
@@ -183,6 +194,9 @@
const pointsPerSeries = 28; const pointsPerSeries = 28;
const summaryPointsPerSeries = 42; const summaryPointsPerSeries = 42;
const signalRenderTickMs = 1200; const signalRenderTickMs = 1200;
const summaryReleaseForceEpsilon = 0.1;
const releaseClearDelayMs = 5000;
const spatialForceMagnitudeEpsilon = 0.0001;
const replayDefaultFrameMs = 40; const replayDefaultFrameMs = 40;
const showSignalPanels = false; const showSignalPanels = false;
const mockToneCycle: SignalTone[] = ["cyan", "lime", "orange", "violet", "gold", "rose"]; const mockToneCycle: SignalTone[] = ["cyan", "lime", "orange", "violet", "gold", "rose"];
@@ -229,9 +243,6 @@
let sampleRateValue = "100Hz"; let sampleRateValue = "100Hz";
let channelsValue = "84"; let channelsValue = "84";
let isWindowMaximized = false; let isWindowMaximized = false;
let activeConfigLinkId = "stream-on";
let isConfigPanelOpen = false;
let isPrecisionTestOpen = false;
let hasSignalData = false; let hasSignalData = false;
let signalPanels: HudSignalPanel[] = buildInactivePanels(); let signalPanels: HudSignalPanel[] = buildInactivePanels();
let summary: HudSummary = buildEmptySummary(); let summary: HudSummary = buildEmptySummary();
@@ -243,7 +254,6 @@
let rangeMin = DEFAULT_PRESSURE_RANGE_MIN; let rangeMin = DEFAULT_PRESSURE_RANGE_MIN;
let rangeMax = DEFAULT_PRESSURE_RANGE_MAX; let rangeMax = DEFAULT_PRESSURE_RANGE_MAX;
let colorMapPreset: PressureColorMapPreset = "emerald"; let colorMapPreset: PressureColorMapPreset = "emerald";
let matrixDisplayMode: MatrixDisplayMode = "dots";
let stageViewMode: StageViewMode = "webgl"; let stageViewMode: StageViewMode = "webgl";
let replayFrames: ReplayFrame[] = []; let replayFrames: ReplayFrame[] = [];
let replayCurrentIndex = 0; let replayCurrentIndex = 0;
@@ -253,6 +263,8 @@
let replayProgress = 0; let replayProgress = 0;
let replayFileName = ""; let replayFileName = "";
let replayTimerId: number | null = null; let replayTimerId: number | null = null;
let replayPendingDevkitSeq: number | null = null;
let replayDevkitSeqCounter = 0;
let fileExplorerOpen = false; let fileExplorerOpen = false;
let fileExplorerMode: FileExplorerMode = "open"; let fileExplorerMode: FileExplorerMode = "open";
let fileExplorerBusy = false; let fileExplorerBusy = false;
@@ -279,17 +291,14 @@
} | null = null; } | null = null;
let devkitStatusTimer: number | null = null; let devkitStatusTimer: number | null = null;
let devkitSpatialForceClearTimer: number | null = null; let devkitSpatialForceClearTimer: number | null = null;
let summaryClearTimer: number | null = null;
let spatialForceClearTimer: number | null = null;
let summaryReleaseHidden = true;
let spatialForceReleaseHidden = false;
let spatialForcePanelVisible = false;
let sessionStartedAt: number = Date.now(); let sessionStartedAt: number = Date.now();
$: uiCopy = copyByLocale[locale]; $: uiCopy = copyByLocale[locale];
$: configLinks = buildConfigLinks(
locale,
activeConfigLinkId,
isConfigPanelOpen,
isPrecisionTestOpen,
devkitEnabled,
isDevKitConfigOpen
);
$: leftSignalPanels = signalPanels.filter((panel) => panel.side === "left"); $: leftSignalPanels = signalPanels.filter((panel) => panel.side === "left");
$: rightSignalPanels = signalPanels.filter((panel) => panel.side === "right"); $: rightSignalPanels = signalPanels.filter((panel) => panel.side === "right");
$: rangeTicks = buildRangeTicks(rangeMin, rangeMax); $: rangeTicks = buildRangeTicks(rangeMin, rangeMax);
@@ -297,6 +306,7 @@
$: rangeScaleStyle = buildRangeScaleStyle(colorMapPreset); $: rangeScaleStyle = buildRangeScaleStyle(colorMapPreset);
$: replayHasData = replayFrames.length > 0; $: replayHasData = replayFrames.length > 0;
$: replayFrameInfo = replayHasData ? `${replayHasDisplayedFrame ? replayCurrentIndex + 1 : 0}/${replayFrames.length}` : ""; $: replayFrameInfo = replayHasData ? `${replayHasDisplayedFrame ? replayCurrentIndex + 1 : 0}/${replayFrames.length}` : "";
$: summaryReleasePending = summaryClearTimer != null;
$: fileExplorerTitle = $: fileExplorerTitle =
fileExplorerMode === "open" ? uiCopy.fileExplorerImportTitle : uiCopy.fileExplorerExportTitle; fileExplorerMode === "open" ? uiCopy.fileExplorerImportTitle : uiCopy.fileExplorerExportTitle;
$: fileExplorerConfirmLabel = $: fileExplorerConfirmLabel =
@@ -306,17 +316,17 @@
return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window; return typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
} }
function serialProtocolForStage(mode: StageViewMode = stageViewMode): "tactileA" | "handGateway" {
return mode === "model3d" ? "handGateway" : "tactileA";
}
function clearDevkitSpatialForce(): void { function clearDevkitSpatialForce(): void {
devkitSpatialForce = null; devkitSpatialForce = null;
if (devkitSpatialForceClearTimer != null && typeof window !== "undefined") { if (devkitSpatialForceClearTimer != null && typeof window !== "undefined") {
window.clearTimeout(devkitSpatialForceClearTimer); window.clearTimeout(devkitSpatialForceClearTimer);
devkitSpatialForceClearTimer = null; devkitSpatialForceClearTimer = null;
} }
hasSignalData = signalPanels.length > 0 || summary.points.length > 0 || spatialForce !== null; hasSignalData =
signalPanels.length > 0 ||
summary.points.length > 0 ||
spatialForce !== null ||
spatialForcePanelVisible;
} }
function scheduleDevkitSpatialForceClear(): void { function scheduleDevkitSpatialForceClear(): void {
@@ -335,6 +345,54 @@
}, 420); }, 420);
} }
function cancelSummaryClear(): void {
if (summaryClearTimer != null && typeof window !== "undefined") {
window.clearTimeout(summaryClearTimer);
}
summaryClearTimer = null;
}
function scheduleSummaryClear(): void {
if (typeof window === "undefined" || summary.points.length === 0) {
return;
}
if (summaryClearTimer != null) {
return;
}
summaryClearTimer = window.setTimeout(() => {
summary = buildEmptySummary();
summaryClearTimer = null;
summaryReleaseHidden = true;
hasSignalData = signalPanels.length > 0 || spatialForce !== null || spatialForcePanelVisible;
}, releaseClearDelayMs);
}
function cancelSpatialForceClear(): void {
if (spatialForceClearTimer != null && typeof window !== "undefined") {
window.clearTimeout(spatialForceClearTimer);
}
spatialForceClearTimer = null;
}
function scheduleSpatialForceClear(): void {
if (typeof window === "undefined" || !spatialForcePanelVisible) {
return;
}
if (spatialForceClearTimer != null) {
return;
}
spatialForceClearTimer = window.setTimeout(() => {
spatialForceClearTimer = null;
spatialForceReleaseHidden = true;
spatialForcePanelVisible = false;
hasSignalData = signalPanels.length > 0 || summary.points.length > 0 || spatialForce !== null;
}, releaseClearDelayMs);
}
function clamp(value: number, min: number, max: number): number { function clamp(value: number, min: number, max: number): number {
return Math.min(max, Math.max(min, value)); return Math.min(max, Math.max(min, value));
} }
@@ -687,7 +745,8 @@
}); });
frames = result.frames.map((frame) => ({ frames = result.frames.map((frame) => ({
values: frame.data, values: frame.data,
dtsMs: frame.dtsMs dtsMs: frame.dtsMs,
spatialForce: frame.spatialForce
})); }));
importedFrameCount = result.frameCount; importedFrameCount = result.frameCount;
importedChannelCount = result.channelCount; importedChannelCount = result.channelCount;
@@ -717,7 +776,8 @@
const frames = result.frames.map((frame) => ({ const frames = result.frames.map((frame) => ({
values: frame.data, values: frame.data,
dtsMs: frame.dtsMs dtsMs: frame.dtsMs,
spatialForce: frame.spatialForce
})); }));
applyImportedFrames(result.fileName, frames, result.frameCount, result.channelCount); applyImportedFrames(result.fileName, frames, result.frameCount, result.channelCount);
@@ -738,6 +798,43 @@
replayTimerId = null; replayTimerId = null;
} }
function buildReplayDevkitSeq(frameIndex: number): number {
replayDevkitSeqCounter = (replayDevkitSeqCounter + 1) % 1000;
return Date.now() * 1000 + replayDevkitSeqCounter + frameIndex;
}
function canPushReplayFrameToDevkit(frame: ReplayFrame): boolean {
return (
isTauriRuntime() &&
devkitEnabled &&
devkitRunning &&
frame.values.length === 84
);
}
function pushReplayFrameToDevkit(frame: ReplayFrame, frameIndex: number): void {
if (!canPushReplayFrameToDevkit(frame)) {
replayPendingDevkitSeq = null;
clearDevkitSpatialForce();
return;
}
const seq = buildReplayDevkitSeq(frameIndex);
replayPendingDevkitSeq = seq;
clearDevkitSpatialForce();
void invoke<DevKitReplayFramePushResult>("devkit_push_replay_frame", {
values: frame.values.map((value) => Math.max(0, Math.round(Number(value) || 0))),
dtsMs: clamp(Math.round(frame.dtsMs), 0, 4_294_967_295),
seq
}).catch((error) => {
if (replayPendingDevkitSeq === seq) {
replayPendingDevkitSeq = null;
}
console.error("Failed to push replay frame to DevKit:", error);
});
}
function frameValuesToMatrix(values: number[]): number[] { function frameValuesToMatrix(values: number[]): number[] {
const totalCells = Math.max(matrixRows * matrixCols, 1); const totalCells = Math.max(matrixRows * matrixCols, 1);
const matrix = new Array<number>(totalCells).fill(0); const matrix = new Array<number>(totalCells).fill(0);
@@ -756,8 +853,13 @@
} }
function resetReplayVisualState(): void { function resetReplayVisualState(): void {
cancelSummaryClear();
cancelSpatialForceClear();
spatialForceReleaseHidden = false;
spatialForcePanelVisible = false;
pressureMatrix = buildZeroMatrix(); pressureMatrix = buildZeroMatrix();
spatialForce = null; spatialForce = null;
replayPendingDevkitSeq = null;
clearDevkitSpatialForce(); clearDevkitSpatialForce();
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildEmptySummary(); summary = buildEmptySummary();
@@ -793,9 +895,14 @@
replayCurrentIndex = safeIndex; replayCurrentIndex = safeIndex;
replayHasDisplayedFrame = true; replayHasDisplayedFrame = true;
replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1; replayProgress = replayFrames.length > 1 ? safeIndex / (replayFrames.length - 1) : 1;
pressureMatrix = frameValuesToMatrix(replayFrames[safeIndex].values); const frame = replayFrames[safeIndex];
spatialForce = null; cancelSummaryClear();
clearDevkitSpatialForce(); cancelSpatialForceClear();
spatialForceReleaseHidden = false;
pressureMatrix = frameValuesToMatrix(frame.values);
spatialForce = frame.spatialForce ?? null;
spatialForcePanelVisible = spatialForce !== null;
pushReplayFrameToDevkit(frame, safeIndex);
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildReplaySummaryAt(safeIndex); summary = buildReplaySummaryAt(safeIndex);
hasSignalData = true; hasSignalData = true;
@@ -964,13 +1071,99 @@
} }
function isZeroLikeValue(value: number): boolean { function isZeroLikeValue(value: number): boolean {
return !Number.isFinite(value) || Math.abs(value) < 0.0001; return !Number.isFinite(value) || Math.abs(value) <= summaryReleaseForceEpsilon;
} }
function shouldHideSummary(points: number[]): boolean { function shouldHideSummary(points: number[]): boolean {
return points.length === 0 || points.every((value) => isZeroLikeValue(value)); return points.length === 0 || points.every((value) => isZeroLikeValue(value));
} }
function latestSummaryValue(summaryValue: HudSummary): number | null {
if (Number.isFinite(summaryValue.latest)) {
return summaryValue.latest;
}
const latestPoint = summaryValue.points[summaryValue.points.length - 1];
return Number.isFinite(latestPoint) ? latestPoint : null;
}
function hasActiveSummary(summaryValue: HudSummary): boolean {
const latest = latestSummaryValue(summaryValue);
return latest !== null && !isZeroLikeValue(latest);
}
function hasActiveSpatialForce(spatialForceValue: HudSpatialForce | null): boolean {
return (
spatialForceValue !== null &&
Number.isFinite(spatialForceValue.angleDeg) &&
Number.isFinite(spatialForceValue.magnitude) &&
Math.abs(spatialForceValue.magnitude) > spatialForceMagnitudeEpsilon
);
}
function buildPacketSummary(summaryValue: HudSummary): HudSummary {
const nowSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10;
const pointCount = summaryValue.points.length;
const spacing =
pointCount > 1 ? Math.min(1.2, nowSeconds / Math.max(pointCount - 1, 1)) : 0;
const startX = Math.max(0, nowSeconds - spacing * Math.max(pointCount - 1, 0));
const xValues = summaryValue.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10);
return { ...summaryValue, xValues };
}
function estimateSummarySpacing(xValues: number[]): number {
const diffs = xValues
.slice(1)
.map((value, index) => value - xValues[index])
.filter((value) => Number.isFinite(value) && value > 0);
if (diffs.length === 0) {
return 1.2;
}
const average = diffs.reduce((sum, value) => sum + value, 0) / diffs.length;
return Math.max(0.1, Math.min(1.2, Math.round(average * 10) / 10));
}
function buildContinuousSummary(summaryValue: HudSummary): HudSummary {
if (
summary.points.length === 0 ||
!summary.xValues ||
summary.xValues.length !== summary.points.length
) {
return buildPacketSummary(summaryValue);
}
const pointCount = summaryValue.points.length;
const previousXValues = summary.xValues;
const previousCount = previousXValues.length;
if (pointCount === 0) {
return buildEmptySummary();
}
if (pointCount < previousCount) {
return buildPacketSummary(summaryValue);
}
const spacing = estimateSummarySpacing(previousXValues);
let xValues: number[];
if (pointCount > previousCount) {
xValues = previousXValues.slice();
while (xValues.length < pointCount) {
const previousX = xValues[xValues.length - 1] ?? 0;
xValues.push(Math.round((previousX + spacing) * 10) / 10);
}
} else {
xValues = previousXValues.slice(1);
const previousX = previousXValues[previousXValues.length - 1] ?? 0;
xValues.push(Math.round((previousX + spacing) * 10) / 10);
}
return { ...summaryValue, xValues };
}
function normalizeSummary(summaryValue: HudSummary): HudSummary { function normalizeSummary(summaryValue: HudSummary): HudSummary {
return shouldHideSummary(summaryValue.points) ? buildEmptySummary() : summaryValue; return shouldHideSummary(summaryValue.points) ? buildEmptySummary() : summaryValue;
} }
@@ -1038,27 +1231,48 @@
return; return;
} }
signalPanels = showSignalPanels ? packet.panels : buildInactivePanels(); signalPanels = showSignalPanels ? packet.panels : buildInactivePanels();
const packetHasActiveSummary = hasActiveSummary(packet.summary);
if (packet.summary.points.length > 0) { if (packet.summary.points.length > 0) {
const nowSeconds = Math.round((Date.now() - sessionStartedAt) / 100) / 10; if (packetHasActiveSummary) {
const pointCount = packet.summary.points.length; summaryReleaseHidden = false;
const spacing = summary = buildContinuousSummary(packet.summary);
pointCount > 1 ? Math.min(1.2, nowSeconds / Math.max(pointCount - 1, 1)) : 0; cancelSummaryClear();
const startX = Math.max(0, nowSeconds - spacing * Math.max(pointCount - 1, 0)); } else if (summaryReleaseHidden) {
const xValues = packet.summary.points.map((_, index) => Math.round((startX + index * spacing) * 10) / 10); summary = buildEmptySummary();
summary = { ...packet.summary, xValues }; } else {
} else { summary = buildContinuousSummary(packet.summary);
summary = packet.summary; scheduleSummaryClear();
}
} else if (!summaryReleaseHidden) {
scheduleSummaryClear();
} }
pressureMatrix = packet.pressureMatrix; pressureMatrix = packet.pressureMatrix;
spatialForce = packet.spatialForce ?? null; const nextSpatialForce = packet.spatialForce ?? null;
if (packetHasActiveSummary && hasActiveSpatialForce(nextSpatialForce)) {
spatialForceReleaseHidden = false;
spatialForcePanelVisible = true;
spatialForce = nextSpatialForce;
cancelSpatialForceClear();
} else {
spatialForce = null;
if (spatialForceReleaseHidden || !spatialForcePanelVisible) {
spatialForcePanelVisible = false;
}
scheduleSpatialForceClear();
}
hasSignalData = hasSignalData =
signalPanels.length > 0 || signalPanels.length > 0 ||
packet.summary.points.length > 0 || summary.points.length > 0 ||
spatialForce !== null || spatialForce !== null ||
devkitSpatialForce !== null; spatialForcePanelVisible;
} }
function clearHudPanels(): void { function clearHudPanels(): void {
cancelSummaryClear();
cancelSpatialForceClear();
summaryReleaseHidden = true;
spatialForceReleaseHidden = false;
spatialForcePanelVisible = false;
hasSignalData = false; hasSignalData = false;
signalPanels = buildInactivePanels(); signalPanels = buildInactivePanels();
summary = buildEmptySummary(); summary = buildEmptySummary();
@@ -1089,69 +1303,6 @@
}); });
} }
function buildConfigLinks(
currentLocale: LocaleCode,
activeId: string,
isSettingsOpen: boolean,
isPrecisionOpen: boolean,
isDevKitEnabled: boolean,
isDevKitOpen: boolean
): HudConfigLink[] {
const labels =
currentLocale === "zh-CN"
? {
streamOn: "打开",
streamOff: "关闭",
precisionTest: "游戏",
settings: "参数"
}
: {
streamOn: "Open",
streamOff: "Close",
precisionTest: "Game",
settings: "Setup"
};
const devkitLabel = currentLocale === "zh-CN" ? "开发工具" : "DevKit";
const links: HudConfigLink[] = [
{
id: "stream-on",
label: labels.streamOn,
tone: "lime",
active: activeId === "stream-on"
},
{
id: "stream-off",
label: labels.streamOff,
tone: "orange",
active: activeId === "stream-off"
},
{
id: "precision-test",
label: labels.precisionTest,
tone: "lime",
active: isPrecisionOpen
},
{
id: "settings",
label: labels.settings,
tone: "neutral",
active: isSettingsOpen
}
];
if (isDevKitEnabled) {
links.push({
id: "devkit",
label: devkitLabel,
tone: "cyan",
active: isDevKitOpen
});
}
return links;
}
async function ensureDefaultWindowSize(): Promise<void> { async function ensureDefaultWindowSize(): Promise<void> {
if (!isTauriRuntime()) { if (!isTauriRuntime()) {
return; return;
@@ -1449,10 +1600,7 @@
connectionNotice = ""; connectionNotice = "";
try { try {
const result = await invoke<SerialConnectResult>("serial_connect", { const result = await invoke<SerialConnectResult>("serial_connect", { port: event.detail });
port: event.detail,
protocol: serialProtocolForStage()
});
connectionState = result.connected ? "online" : "offline"; connectionState = result.connected ? "online" : "offline";
serialPortValue = result.port; serialPortValue = result.port;
connectionNotice = ""; connectionNotice = "";
@@ -1704,37 +1852,6 @@
resetReplayVisualState(); resetReplayVisualState();
} }
function handleConfigLink(event: CustomEvent<string>): void {
if (event.detail === "precision-test") {
stageViewMode = "webgl";
isPrecisionTestOpen = !isPrecisionTestOpen;
isConfigPanelOpen = false;
isDevKitConfigOpen = false;
return;
}
if (event.detail === "settings") {
stageViewMode = "webgl";
isPrecisionTestOpen = false;
isConfigPanelOpen = !isConfigPanelOpen;
isDevKitConfigOpen = false;
return;
}
if (event.detail === "devkit") {
isPrecisionTestOpen = false;
isConfigPanelOpen = false;
isDevKitConfigOpen = !isDevKitConfigOpen;
return;
}
isPrecisionTestOpen = false;
isConfigPanelOpen = false;
isDevKitConfigOpen = false;
activeConfigLinkId = event.detail;
console.info("[hud] config link clicked:", event.detail);
}
async function handleWindowControl(event: CustomEvent<WindowControlAction>): Promise<void> { async function handleWindowControl(event: CustomEvent<WindowControlAction>): Promise<void> {
if (!isTauriRuntime()) { if (!isTauriRuntime()) {
return; return;
@@ -1803,52 +1920,13 @@
} }
} }
function handleMatrixDisplayToggle(event: CustomEvent<boolean>): void { function handleStageModeChange(event: CustomEvent<StageViewMode>): void {
matrixDisplayMode = event.detail ? "dots" : "numeric";
}
async function handleStageModeChange(event: CustomEvent<StageViewMode>): Promise<void> {
const previousMode = stageViewMode;
stageViewMode = event.detail; stageViewMode = event.detail;
if (stageViewMode === "model3d") {
isPrecisionTestOpen = false;
isConfigPanelOpen = false;
}
if (
previousMode === stageViewMode ||
connectionState !== "online" ||
!serialPortValue ||
!isTauriRuntime()
) {
return;
}
connectionState = "connecting";
connectionNotice = "";
try {
await invoke<SerialConnectResult>("serial_disconnect");
const result = await invoke<SerialConnectResult>("serial_connect", {
port: serialPortValue,
protocol: serialProtocolForStage(stageViewMode)
});
connectionState = result.connected ? "online" : "offline";
serialPortValue = result.port;
clearHudPanels();
console.info(`[serial] switched protocol to ${serialProtocolForStage(stageViewMode)}`);
} catch (error) {
connectionState = "offline";
connectionNotice = resolveSerialNotice(error, "connect");
connectionNoticeTone = "warn";
clearHudPanels();
console.error("Serial protocol switch failed:", error);
}
} }
onMount(() => { onMount(() => {
let disposed = false; let disposed = false;
let unlistenHudStream: UnlistenFn | null = null; let unlistenHudStream: UnlistenFn | null = null;
let unlistenHandGatewayRaw: UnlistenFn | null = null;
let unlistenDevkitPztAngle: UnlistenFn | null = null; let unlistenDevkitPztAngle: UnlistenFn | null = null;
let stopMockFeed: (() => void) | null = null; let stopMockFeed: (() => void) | null = null;
@@ -1873,41 +1951,28 @@
.catch((error) => { .catch((error) => {
console.error("Failed to listen for hud_stream:", error); console.error("Failed to listen for hud_stream:", error);
}); });
void listen<number[]>("hand_gateway_raw", (event) => { void listen<DevKitPztAngleEvent>("devkit_pzt_angle", (event) => {
const rawHex = event.payload if (replayHasData) {
.map((byte) => byte.toString(16).padStart(2, "0").toUpperCase()) if (replayPendingDevkitSeq == null || event.payload.seq !== replayPendingDevkitSeq) {
.join(" ");
console.info(`[hand_gateway][raw] ${rawHex}`);
})
.then((unlisten) => {
if (disposed) {
unlisten();
return; return;
} }
replayPendingDevkitSeq = null;
}
unlistenHandGatewayRaw = unlisten;
})
.catch((error) => {
console.error("Failed to listen for hand_gateway_raw:", error);
});
void listen<DevKitPztAngleEvent>("devkit_pzt_angle", (event) => {
const angleDeg = Number(event.payload.angle); const angleDeg = Number(event.payload.angle);
if (!Number.isFinite(angleDeg)) { const magnitude = Number(event.payload.magnitude);
const isReportable = event.payload.state > 0 && Number.isFinite(magnitude) && magnitude > 0;
if (!Number.isFinite(angleDeg) || !isReportable) {
clearDevkitSpatialForce(); clearDevkitSpatialForce();
return; return;
} }
devkitSpatialForce = { devkitSpatialForce = {
angleDeg, angleDeg,
magnitude: 0, magnitude,
confidence: 0 confidence: 0
}; };
scheduleDevkitSpatialForceClear(); scheduleDevkitSpatialForceClear();
hasSignalData =
signalPanels.length > 0 ||
summary.points.length > 0 ||
spatialForce !== null ||
devkitSpatialForce !== null;
}) })
.then((unlisten) => { .then((unlisten) => {
if (disposed) { if (disposed) {
@@ -1927,10 +1992,11 @@
return () => { return () => {
disposed = true; disposed = true;
pauseReplayPlayback(); pauseReplayPlayback();
cancelSummaryClear();
cancelSpatialForceClear();
clearDevkitSpatialForce(); clearDevkitSpatialForce();
stopMockFeed?.(); stopMockFeed?.();
unlistenHudStream?.(); unlistenHudStream?.();
unlistenHandGatewayRaw?.();
unlistenDevkitPztAngle?.(); unlistenDevkitPztAngle?.();
if (devkitStatusTimer != null) { if (devkitStatusTimer != null) {
window.clearInterval(devkitStatusTimer); window.clearInterval(devkitStatusTimer);
@@ -1967,15 +2033,9 @@
sampleRateValue={sampleRateValue} sampleRateValue={sampleRateValue}
channelsLabel={uiCopy.channelsLabel} channelsLabel={uiCopy.channelsLabel}
channelsValue={channelsValue} channelsValue={channelsValue}
configLinksLabel={uiCopy.configLinksLabel}
refreshPortsLabel={uiCopy.refreshPortsLabel} refreshPortsLabel={uiCopy.refreshPortsLabel}
matrixViewLabel={uiCopy.matrixViewLabel} matrixViewLabel={uiCopy.matrixViewLabel}
matrixViewNumericLabel={uiCopy.matrixViewNumericLabel}
matrixViewDotsLabel={uiCopy.matrixViewDotsLabel} matrixViewDotsLabel={uiCopy.matrixViewDotsLabel}
{matrixDisplayMode}
stageModeLabel={uiCopy.stageModeLabel}
stageModeWebglLabel={uiCopy.stageModeWebglLabel}
stageModeModelLabel={uiCopy.stageModeModelLabel}
{stageViewMode} {stageViewMode}
connectActionLabel={uiCopy.connectActionLabel} connectActionLabel={uiCopy.connectActionLabel}
disconnectActionLabel={uiCopy.disconnectActionLabel} disconnectActionLabel={uiCopy.disconnectActionLabel}
@@ -1988,7 +2048,6 @@
noticeCancelLabel={locale === "zh-CN" ? "取消" : "Cancel"} noticeCancelLabel={locale === "zh-CN" ? "取消" : "Cancel"}
noticeShowActions={updateNoticeVisible} noticeShowActions={updateNoticeVisible}
noticeActionBusy={updateInstallBusy} noticeActionBusy={updateInstallBusy}
{configLinks}
{isRefreshingPorts} {isRefreshingPorts}
{isExporting} {isExporting}
isConnectDisabled={!serialPortValue || connectionState === "connecting"} isConnectDisabled={!serialPortValue || connectionState === "connecting"}
@@ -1997,8 +2056,6 @@
on:windowcontrol={handleWindowControl} on:windowcontrol={handleWindowControl}
on:localechange={handleLocaleChange} on:localechange={handleLocaleChange}
on:portchange={handlePortChange} on:portchange={handlePortChange}
on:configlink={handleConfigLink}
on:matrixdisplaytoggle={handleMatrixDisplayToggle}
on:stagemodechange={handleStageModeChange} on:stagemodechange={handleStageModeChange}
on:serialrefresh={handleSerialRefresh} on:serialrefresh={handleSerialRefresh}
on:serialconnect={handleSerialConnect} on:serialconnect={handleSerialConnect}
@@ -2019,7 +2076,6 @@
bind:rangeMin bind:rangeMin
bind:rangeMax bind:rangeMax
bind:colorMapPreset bind:colorMapPreset
bind:matrixDisplayMode
{stageViewMode} {stageViewMode}
configPanelTitle={uiCopy.configPanelTitle} configPanelTitle={uiCopy.configPanelTitle}
configPanelHint={uiCopy.configPanelHint} configPanelHint={uiCopy.configPanelHint}
@@ -2042,24 +2098,23 @@
{replayFileName} {replayFileName}
{replayFrameInfo} {replayFrameInfo}
{sessionStartedAt} {sessionStartedAt}
{summaryReleasePending}
{spatialForcePanelVisible}
resetConfigLabel={uiCopy.resetConfigLabel} resetConfigLabel={uiCopy.resetConfigLabel}
applyLiveHint={uiCopy.applyLiveHint} applyLiveHint={uiCopy.applyLiveHint}
leftPanels={leftSignalPanels} leftPanels={leftSignalPanels}
rightPanels={rightSignalPanels} rightPanels={rightSignalPanels}
{pressureMatrix} {pressureMatrix}
{spatialForce} {spatialForce}
{devkitSpatialForce} showConfigPanel={false}
showConfigPanel={isConfigPanelOpen}
showPrecisionTestPanel={isPrecisionTestOpen}
{summary} {summary}
on:replaytoggle={handleReplayToggle} on:replaytoggle={handleReplayToggle}
on:replaystop={handleReplayStop} on:replaystop={handleReplayStop}
on:replayseek={handleReplaySeek} on:replayseek={handleReplaySeek}
on:replayspeed={handleReplaySpeed} on:replayspeed={handleReplaySpeed}
on:replayclose={handleReplayClose} on:replayclose={handleReplayClose}
on:configclose={() => (isConfigPanelOpen = false)}
> >
{#if !isPrecisionTestOpen && stageViewMode === "webgl"} {#if stageViewMode === "webgl"}
<section class="range-scale" aria-label="Signal Range"> <section class="range-scale" aria-label="Signal Range">
<p class="range-label">{locale === "zh-CN" ? "范围" : "Range"}</p> <p class="range-label">{locale === "zh-CN" ? "范围" : "Range"}</p>
<div class="range-track"> <div class="range-track">