Compare commits

...

4 Commits

Author SHA1 Message Date
9f23776d5a 新打包程序 2026-02-10 17:58:45 +08:00
d83ca7557e 修改获取截图时间文件路径为D:\\uploadInfo\\Logs\\ 2026-02-10 17:49:56 +08:00
119bb845b4 截图不全改回true 2026-02-10 17:39:58 +08:00
6274c83dd5 修改截图保存文件夹 2026-02-10 14:41:13 +08:00
31 changed files with 26820 additions and 696 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,293 @@
('D:\\Projects\\cjgc_screenshot\\dist\\沉降观测自动截图.exe',
True,
False,
False,
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\bootloader\\images\\icon-console.ico',
None,
False,
False,
b'<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n<assembly xmlns='
b'"urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">\n <trustInfo x'
b'mlns="urn:schemas-microsoft-com:asm.v3">\n <security>\n <requested'
b'Privileges>\n <requestedExecutionLevel level="asInvoker" uiAccess='
b'"false"/>\n </requestedPrivileges>\n </security>\n </trustInfo>\n '
b'<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">\n <'
b'application>\n <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f'
b'0}"/>\n <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>\n '
b' <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>\n <s'
b'upportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>\n <supporte'
b'dOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>\n </application>\n <'
b'/compatibility>\n <application xmlns="urn:schemas-microsoft-com:asm.v3">'
b'\n <windowsSettings>\n <longPathAware xmlns="http://schemas.micros'
b'oft.com/SMI/2016/WindowsSettings">true</longPathAware>\n </windowsSett'
b'ings>\n </application>\n <dependency>\n <dependentAssembly>\n <ass'
b'emblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version='
b'"6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" langua'
b'ge="*"/>\n </dependentAssembly>\n </dependency>\n</assembly>',
True,
False,
None,
None,
None,
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\沉降观测自动截图.pkg',
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\PYZ-00.pyz',
'PYZ'),
('struct',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\struct.pyc',
'PYMODULE'),
('pyimod01_archive',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth_inspect',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('scheduler', 'D:\\Projects\\cjgc_screenshot\\scheduler.py', 'PYSOURCE'),
('selenium\\webdriver\\common\\windows\\selenium-manager.exe',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\windows\\selenium-manager.exe',
'BINARY'),
('python312.dll', 'C:\\Program Files\\Python312\\python312.dll', 'BINARY'),
('select.pyd', 'C:\\Program Files\\Python312\\DLLs\\select.pyd', 'EXTENSION'),
('_multiprocessing.pyd',
'C:\\Program Files\\Python312\\DLLs\\_multiprocessing.pyd',
'EXTENSION'),
('pyexpat.pyd',
'C:\\Program Files\\Python312\\DLLs\\pyexpat.pyd',
'EXTENSION'),
('_ssl.pyd', 'C:\\Program Files\\Python312\\DLLs\\_ssl.pyd', 'EXTENSION'),
('_hashlib.pyd',
'C:\\Program Files\\Python312\\DLLs\\_hashlib.pyd',
'EXTENSION'),
('unicodedata.pyd',
'C:\\Program Files\\Python312\\DLLs\\unicodedata.pyd',
'EXTENSION'),
('_decimal.pyd',
'C:\\Program Files\\Python312\\DLLs\\_decimal.pyd',
'EXTENSION'),
('_socket.pyd',
'C:\\Program Files\\Python312\\DLLs\\_socket.pyd',
'EXTENSION'),
('_lzma.pyd', 'C:\\Program Files\\Python312\\DLLs\\_lzma.pyd', 'EXTENSION'),
('_bz2.pyd', 'C:\\Program Files\\Python312\\DLLs\\_bz2.pyd', 'EXTENSION'),
('_ctypes.pyd',
'C:\\Program Files\\Python312\\DLLs\\_ctypes.pyd',
'EXTENSION'),
('_queue.pyd', 'C:\\Program Files\\Python312\\DLLs\\_queue.pyd', 'EXTENSION'),
('_overlapped.pyd',
'C:\\Program Files\\Python312\\DLLs\\_overlapped.pyd',
'EXTENSION'),
('_asyncio.pyd',
'C:\\Program Files\\Python312\\DLLs\\_asyncio.pyd',
'EXTENSION'),
('charset_normalizer\\md__mypyc.cp312-win_amd64.pyd',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\charset_normalizer\\md__mypyc.cp312-win_amd64.pyd',
'EXTENSION'),
('charset_normalizer\\md.cp312-win_amd64.pyd',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\charset_normalizer\\md.cp312-win_amd64.pyd',
'EXTENSION'),
('_uuid.pyd', 'C:\\Program Files\\Python312\\DLLs\\_uuid.pyd', 'EXTENSION'),
('_wmi.pyd', 'C:\\Program Files\\Python312\\DLLs\\_wmi.pyd', 'EXTENSION'),
('api-ms-win-core-synch-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-synch-l1-2-0.dll',
'BINARY'),
('api-ms-win-crt-filesystem-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-filesystem-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-conio-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-conio-l1-1-0.dll',
'BINARY'),
('VCRUNTIME140.dll',
'C:\\Program Files\\Python312\\VCRUNTIME140.dll',
'BINARY'),
('api-ms-win-crt-locale-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-math-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-math-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-convert-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-time-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-time-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-string-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-stdio-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-stdio-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-runtime-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-runtime-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-environment-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-process-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-process-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-heap-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-utility-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-utility-l1-1-0.dll',
'BINARY'),
('libcrypto-3.dll',
'C:\\Program Files\\Python312\\DLLs\\libcrypto-3.dll',
'BINARY'),
('libssl-3.dll',
'C:\\Program Files\\Python312\\DLLs\\libssl-3.dll',
'BINARY'),
('libffi-8.dll',
'C:\\Program Files\\Python312\\DLLs\\libffi-8.dll',
'BINARY'),
('ucrtbase.dll', 'D:\\Resource\\JDK\\JavaJDK17\\bin\\ucrtbase.dll', 'BINARY'),
('api-ms-win-core-debug-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-errorhandling-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-console-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-synch-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-interlocked-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processenvironment-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processenvironment-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-1.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-string-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-sysinfo-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-timezone-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-handle-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-libraryloader-l1-1-0.dll',
'BINARY'),
('certifi\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\certifi\\py.typed',
'DATA'),
('certifi\\cacert.pem',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\certifi\\cacert.pem',
'DATA'),
('selenium\\webdriver\\common\\mutation-listener.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\mutation-listener.js',
'DATA'),
('selenium\\webdriver\\remote\\getAttribute.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\getAttribute.js',
'DATA'),
('selenium\\webdriver\\firefox\\webdriver_prefs.json',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\firefox\\webdriver_prefs.json',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v137\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v137\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v138\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v138\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\linux\\selenium-manager',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\linux\\selenium-manager',
'DATA'),
('selenium\\webdriver\\remote\\findElements.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\findElements.js',
'DATA'),
('selenium\\webdriver\\remote\\isDisplayed.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\isDisplayed.js',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v139\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v139\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\macos\\selenium-manager',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\macos\\selenium-manager',
'DATA'),
('selenium\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\py.typed',
'DATA'),
('base_library.zip',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\base_library.zip',
'DATA')],
[],
False,
False,
1770717074,
[('run.exe',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\bootloader\\Windows-64bit-intel\\run.exe',
'EXECUTABLE')],
'C:\\Program Files\\Python312\\python312.dll')

View File

@@ -0,0 +1,269 @@
('D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\沉降观测自动截图.pkg',
{'BINARY': True,
'DATA': True,
'EXECUTABLE': True,
'EXTENSION': True,
'PYMODULE': True,
'PYSOURCE': True,
'PYZ': False,
'SPLASH': True,
'SYMLINK': False},
[('pyi-contents-directory _internal', '', 'OPTION'),
('PYZ-00.pyz',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\PYZ-00.pyz',
'PYZ'),
('struct',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\struct.pyc',
'PYMODULE'),
('pyimod01_archive',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod01_archive.pyc',
'PYMODULE'),
('pyimod02_importers',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod02_importers.pyc',
'PYMODULE'),
('pyimod03_ctypes',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod03_ctypes.pyc',
'PYMODULE'),
('pyimod04_pywin32',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\localpycs\\pyimod04_pywin32.pyc',
'PYMODULE'),
('pyiboot01_bootstrap',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\loader\\pyiboot01_bootstrap.py',
'PYSOURCE'),
('pyi_rth_inspect',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_inspect.py',
'PYSOURCE'),
('pyi_rth_pkgutil',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_pkgutil.py',
'PYSOURCE'),
('pyi_rth_multiprocessing',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\PyInstaller\\hooks\\rthooks\\pyi_rth_multiprocessing.py',
'PYSOURCE'),
('scheduler', 'D:\\Projects\\cjgc_screenshot\\scheduler.py', 'PYSOURCE'),
('selenium\\webdriver\\common\\windows\\selenium-manager.exe',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\windows\\selenium-manager.exe',
'BINARY'),
('python312.dll', 'C:\\Program Files\\Python312\\python312.dll', 'BINARY'),
('select.pyd', 'C:\\Program Files\\Python312\\DLLs\\select.pyd', 'EXTENSION'),
('_multiprocessing.pyd',
'C:\\Program Files\\Python312\\DLLs\\_multiprocessing.pyd',
'EXTENSION'),
('pyexpat.pyd',
'C:\\Program Files\\Python312\\DLLs\\pyexpat.pyd',
'EXTENSION'),
('_ssl.pyd', 'C:\\Program Files\\Python312\\DLLs\\_ssl.pyd', 'EXTENSION'),
('_hashlib.pyd',
'C:\\Program Files\\Python312\\DLLs\\_hashlib.pyd',
'EXTENSION'),
('unicodedata.pyd',
'C:\\Program Files\\Python312\\DLLs\\unicodedata.pyd',
'EXTENSION'),
('_decimal.pyd',
'C:\\Program Files\\Python312\\DLLs\\_decimal.pyd',
'EXTENSION'),
('_socket.pyd',
'C:\\Program Files\\Python312\\DLLs\\_socket.pyd',
'EXTENSION'),
('_lzma.pyd', 'C:\\Program Files\\Python312\\DLLs\\_lzma.pyd', 'EXTENSION'),
('_bz2.pyd', 'C:\\Program Files\\Python312\\DLLs\\_bz2.pyd', 'EXTENSION'),
('_ctypes.pyd',
'C:\\Program Files\\Python312\\DLLs\\_ctypes.pyd',
'EXTENSION'),
('_queue.pyd', 'C:\\Program Files\\Python312\\DLLs\\_queue.pyd', 'EXTENSION'),
('_overlapped.pyd',
'C:\\Program Files\\Python312\\DLLs\\_overlapped.pyd',
'EXTENSION'),
('_asyncio.pyd',
'C:\\Program Files\\Python312\\DLLs\\_asyncio.pyd',
'EXTENSION'),
('charset_normalizer\\md__mypyc.cp312-win_amd64.pyd',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\charset_normalizer\\md__mypyc.cp312-win_amd64.pyd',
'EXTENSION'),
('charset_normalizer\\md.cp312-win_amd64.pyd',
'C:\\Program '
'Files\\Python312\\Lib\\site-packages\\charset_normalizer\\md.cp312-win_amd64.pyd',
'EXTENSION'),
('_uuid.pyd', 'C:\\Program Files\\Python312\\DLLs\\_uuid.pyd', 'EXTENSION'),
('_wmi.pyd', 'C:\\Program Files\\Python312\\DLLs\\_wmi.pyd', 'EXTENSION'),
('api-ms-win-core-synch-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-synch-l1-2-0.dll',
'BINARY'),
('api-ms-win-crt-filesystem-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-filesystem-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-conio-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-conio-l1-1-0.dll',
'BINARY'),
('VCRUNTIME140.dll',
'C:\\Program Files\\Python312\\VCRUNTIME140.dll',
'BINARY'),
('api-ms-win-crt-locale-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-locale-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-math-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-math-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-convert-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-convert-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-time-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-time-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-string-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-stdio-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-stdio-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-runtime-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-runtime-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-environment-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-environment-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-process-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-process-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-heap-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-crt-utility-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-crt-utility-l1-1-0.dll',
'BINARY'),
('libcrypto-3.dll',
'C:\\Program Files\\Python312\\DLLs\\libcrypto-3.dll',
'BINARY'),
('libssl-3.dll',
'C:\\Program Files\\Python312\\DLLs\\libssl-3.dll',
'BINARY'),
('libffi-8.dll',
'C:\\Program Files\\Python312\\DLLs\\libffi-8.dll',
'BINARY'),
('ucrtbase.dll', 'D:\\Resource\\JDK\\JavaJDK17\\bin\\ucrtbase.dll', 'BINARY'),
('api-ms-win-core-debug-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-debug-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-profile-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-profile-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-errorhandling-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-errorhandling-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-console-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-console-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-synch-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-synch-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-heap-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-heap-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l2-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l2-1-0.dll',
'BINARY'),
('api-ms-win-core-interlocked-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-interlocked-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-memory-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-memory-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processenvironment-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processenvironment-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processthreads-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-file-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-file-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-localization-l1-2-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-localization-l1-2-0.dll',
'BINARY'),
('api-ms-win-core-processthreads-l1-1-1.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-processthreads-l1-1-1.dll',
'BINARY'),
('api-ms-win-core-namedpipe-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-namedpipe-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-string-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-string-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-sysinfo-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-sysinfo-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-datetime-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-datetime-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-rtlsupport-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-rtlsupport-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-util-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-util-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-timezone-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-timezone-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-handle-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-handle-l1-1-0.dll',
'BINARY'),
('api-ms-win-core-libraryloader-l1-1-0.dll',
'D:\\Resource\\JDK\\JavaJDK17\\bin\\api-ms-win-core-libraryloader-l1-1-0.dll',
'BINARY'),
('certifi\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\certifi\\py.typed',
'DATA'),
('certifi\\cacert.pem',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\certifi\\cacert.pem',
'DATA'),
('selenium\\webdriver\\common\\mutation-listener.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\mutation-listener.js',
'DATA'),
('selenium\\webdriver\\remote\\getAttribute.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\getAttribute.js',
'DATA'),
('selenium\\webdriver\\firefox\\webdriver_prefs.json',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\firefox\\webdriver_prefs.json',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v137\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v137\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v138\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v138\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\linux\\selenium-manager',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\linux\\selenium-manager',
'DATA'),
('selenium\\webdriver\\remote\\findElements.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\findElements.js',
'DATA'),
('selenium\\webdriver\\remote\\isDisplayed.js',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\remote\\isDisplayed.js',
'DATA'),
('selenium\\webdriver\\common\\devtools\\v139\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\devtools\\v139\\py.typed',
'DATA'),
('selenium\\webdriver\\common\\macos\\selenium-manager',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\webdriver\\common\\macos\\selenium-manager',
'DATA'),
('selenium\\py.typed',
'C:\\Users\\ADMIN\\AppData\\Roaming\\Python\\Python312\\site-packages\\selenium\\py.typed',
'DATA'),
('base_library.zip',
'D:\\Projects\\cjgc_screenshot\\build\\沉降观测自动截图\\base_library.zip',
'DATA')],
'python312.dll',
False,
False,
False,
[],
None,
None,
None)

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,64 @@
This file lists modules PyInstaller was not able to find. This does not
necessarily mean this module is required for running your program. Python and
Python 3rd-party packages include a lot of conditional or optional modules. For
example the module 'ntpath' only exists on Windows, whereas the module
'posixpath' only exists on Posix systems.
Types if import:
* top-level: imported at the top-level - look at these first
* conditional: imported within an if-statement
* delayed: imported within a function
* optional: imported within a try-except-statement
IMPORTANT: Do NOT post this list to the issue-tracker. Use it as a basis for
tracking down the missing module yourself. Thanks!
missing module named pwd - imported by posixpath (delayed, conditional, optional), shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), netrc (delayed, conditional), getpass (delayed)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional)
missing module named _posixsubprocess - imported by subprocess (conditional), multiprocessing.util (delayed)
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 _scproxy - imported by urllib.request (conditional)
missing module named termios - imported by getpass (optional)
missing module named _sha512 - imported by random (optional)
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)
excluded module named _frozen_importlib - imported by importlib (optional), importlib.abc (optional), zipimport (top-level)
missing module named posix - imported by os (conditional, optional), posixpath (optional), shutil (conditional), importlib._bootstrap_external (conditional)
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.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.get_start_method - imported by multiprocessing (top-level), multiprocessing.spawn (top-level)
missing module named pyimod02_importers - imported by C:\Program Files\Python312\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed)
missing module named collections.Callable - imported by collections (optional), socks (optional)
missing module named simplejson - imported by requests.compat (conditional, optional)
missing module named dummy_threading - imported by requests.cookies (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 zstandard - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named compression - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named 'h2.events' - imported by urllib3.http2.connection (top-level)
missing module named 'h2.connection' - imported by urllib3.http2.connection (top-level)
missing module named h2 - imported by urllib3.http2.connection (top-level)
missing module named brotli - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named brotlicffi - imported by urllib3.util.request (optional), urllib3.response (optional)
missing module named win_inet_pton - imported by socks (conditional, optional)
missing module named cryptography - imported by urllib3.contrib.pyopenssl (top-level), requests (conditional, optional)
missing module named 'OpenSSL.crypto' - imported by urllib3.contrib.pyopenssl (delayed, conditional)
missing module named 'cryptography.x509' - imported by urllib3.contrib.pyopenssl (delayed, optional)
missing module named OpenSSL - imported by urllib3.contrib.pyopenssl (top-level)
missing module named 'pyodide.ffi' - imported by urllib3.contrib.emscripten.fetch (delayed, optional)
missing module named pyodide - imported by urllib3.contrib.emscripten.fetch (top-level)
missing module named js - imported by urllib3.contrib.emscripten.fetch (top-level)
missing module named wsaccel - imported by websocket._utils (optional)
missing module named 'python_socks.sync' - imported by websocket._http (optional)
missing module named 'python_socks._types' - imported by websocket._http (optional)
missing module named python_socks - imported by websocket._http (optional)
missing module named 'wsaccel.xormask' - imported by websocket._abnf (optional)
missing module named _winreg - imported by platform (delayed, optional), selenium.webdriver.firefox.firefox_binary (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 - imported by platform (delayed)

File diff suppressed because it is too large Load Diff

View File

@@ -1,347 +0,0 @@
import subprocess
import re
import time
import requests
import json
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
from appium.options.android import UiAutomator2Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from urllib3.connection import port_by_scheme
# =======================
# 基础工具函数
# =======================
def run_command(command):
"""执行系统命令并返回输出"""
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout.strip()
# =======================
# API请求函数
# =======================
def get_new_port(yh_id):
"""从服务器获取新的端口号"""
url = "https://engineering.yuxindazhineng.com/index/index/get_yh_port"
headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
data = {
"yh_id": yh_id
}
try:
print(f"🔍 查询服务器新端口号用户ID: {yh_id}")
response = requests.post(url, headers=headers, data=data, timeout=10)
if response.status_code == 200:
result = response.json()
if result.get("code") == 0:
print(f"✅ 查询成功,新端口号: {result.get('data', '未知')}")
return result.get("data", None)
else:
print(f"❌ 查询失败: {result.get('msg', '未知错误')}")
return None
else:
print(f"❌ 服务器响应错误: {response.status_code}")
return None
except requests.exceptions.RequestException as e:
print(f"❌ 网络请求失败: {e}")
return None
def get_accounts_from_server(yh_id):
"""从服务器获取账户信息"""
url = "http://www.yuxindazhineng.com:3002/api/accounts/get_uplaod_data"
headers = {
"Content-Type": "application/json"
}
data = {
"yh_id": yh_id
}
try:
print(f"🔍 查询服务器账户信息用户ID: {yh_id}")
response = requests.post(url, headers=headers, json=data, timeout=10)
if response.status_code == 200:
result = response.json()
if result.get("code") == 0:
print(f"✅ 查询成功,找到 {result.get('total', 0)} 个账户")
return result.get("data", [])
else:
print(f"❌ 查询失败: {result.get('message', '未知错误')}")
return []
else:
print(f"❌ 服务器响应错误: {response.status_code}")
return []
except requests.exceptions.RequestException as e:
print(f"❌ 网络请求失败: {e}")
return []
except json.JSONDecodeError as e:
print(f"❌ JSON解析失败: {e}")
return []
def update_device_info(account_id, device_name, device_port, device_ip):
"""更新设备信息到服务器"""
url = "http://www.yuxindazhineng.com:3002/api/accounts/update"
headers = {
"Content-Type": "application/json"
}
data = {
"account_id": str(account_id),
"account_data": {
"device_name": str(device_name),
"device_port": str(device_port),
"device_ip": str(device_ip)
}
}
try:
print(f"🔄 更新设备信息账户ID: {account_id}")
print(f" 设备信息: 名称={device_name}, 端口={device_port}, IP={device_ip}")
response = requests.post(url, headers=headers, json=data, timeout=10)
if response.status_code == 200:
result = response.json()
if result.get("code") == 0:
print(f"✅ 更新成功: {result.get('message', '未知信息')}")
return True
else:
print(f"❌ 更新失败: {result.get('message', '未知错误')}")
return False
else:
print(f"❌ 服务器响应错误: {response.status_code}")
return False
except requests.exceptions.RequestException as e:
print(f"❌ 网络请求失败: {e}")
return False
# =======================
# Appium 启动
# =======================
def start_appium():
print("🚀 启动 Appium Server ...")
subprocess.Popen(
["appium.cmd", "-a", "127.0.0.1", "-p", "4723"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL
)
time.sleep(5) # 给 Appium 启动时间
print("✅ Appium Server 已启动")
# =======================
# 启动沉降观测 App
# =======================
def start_settlement_app(device_id, device_ip, device_port):
print(f"📱 使用 Appium 连接设备: {device_id}")
options = UiAutomator2Options()
options.platform_name = "Android"
options.device_name = device_id
options.udid = device_id
# ⚠️ TODO替换为你的真实信息
options.app_package = "com.bjjw.cjgc"
options.app_activity = ".activity.LoginActivity"
options.automation_name = "UiAutomator2"
options.no_reset = True
options.auto_grant_permissions = True
options.new_command_timeout = 28800
# 超时增强(无线 ADB 必须)
options.set_capability("uiautomator2ServerLaunchTimeout", 60000)
options.set_capability("adbExecTimeout", 120000)
driver = webdriver.Remote(
"http://127.0.0.1:4723",
options=options
)
# 使用ADB命令启动Activity
try:
adb_command = f"adb -s {device_id} shell am start -n com.bjjw.cjgc/.activity.LoginActivity"
result = subprocess.run(adb_command, shell=True, capture_output=True, text=True)
if result.returncode == 0:
time.sleep(1) # 等待Activity启动
except Exception:
return False
print("✅ 沉降观测 App 已成功启动")
# =======================
# 获取用户名文本框内容
# =======================
app_username = None
try:
print("🔍 尝试获取用户名文本框内容...")
# 创建显式等待对象
wait = WebDriverWait(driver, 15) # 最多等待15秒
# 等待用户名文本框可点击
username_field = wait.until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/et_user_name"))
)
# 获取文本框中的文本内容
app_username = username_field.text
# 如果文本框是空的尝试使用get_attribute获取文本
if not app_username:
app_username = username_field.get_attribute("text")
# 如果还是空的,尝试获取其他属性
if not app_username:
app_username = username_field.get_attribute("content-desc") or username_field.get_attribute("label")
if app_username:
print(f"✅ 成功获取到用户名: {app_username}")
else:
print("⚠️ 用户名文本框为空")
except Exception as e:
print(f"❌ 获取用户名失败: {e}")
return driver, app_username
# =======================
# 无线 ADB 建链主流程
# =======================
def setup_adb_wireless(yh_id="68c0dbfdb7cbcd616e7c5ab5"):
port = get_new_port(yh_id)
# port = 3435
print(f"🚀 开始无线 ADB 建链(端口 {port}")
print(f"📋 用户ID: {yh_id}")
# 从服务器获取账户信息
accounts = get_accounts_from_server(yh_id)
if not accounts:
print("❌ 未从服务器获取到账户信息,终止流程")
return
devices_output = run_command("adb devices")
lines = devices_output.splitlines()[1:]
usb_devices = []
for line in lines:
if not line.strip():
continue
device_id = line.split()[0]
# 跳过已经是无线的
if ":" in device_id:
continue
usb_devices.append(device_id)
if not usb_devices:
print("❌ 未检测到 USB 设备")
return
for serial in usb_devices:
print(f"\n🔎 处理设备: {serial}")
# 获取 WLAN IP
ip_info = run_command(f"adb -s {serial} shell ip addr show wlan0")
ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_info)
if not ip_match:
print("⚠️ 获取 IP 失败,请确认已连接 WiFi")
continue
device_ip = ip_match.group(1)
print(f"📍 设备 IP: {device_ip}")
# 切 TCP 模式
run_command(f"adb -s {serial} tcpip {port}")
time.sleep(2)
# 无线连接
connect_result = run_command(f"adb connect {device_ip}:{port}")
if "connected" not in connect_result.lower():
print(f"❌ 无线连接失败: {connect_result}")
continue
wireless_id = f"{device_ip}:{port}"
print(f"✅ 无线 ADB 成功: {wireless_id}")
# ===== 后续自动化 =====
start_appium()
driver, app_username = start_settlement_app(wireless_id, device_ip, port)
if not app_username:
print("⚠️ 未获取到App中的用户名跳过服务器更新")
continue
# 在账户列表中查找匹配的用户名
matched_account = None
for account in accounts:
if account.get("username") == app_username:
matched_account = account
break
if not matched_account:
print(f"❌ 未找到与用户名 '{app_username}' 匹配的账户")
continue
print(f"✅ 找到匹配账户: {matched_account.get('cl_name')} ({matched_account.get('username')})")
print(f" account_id: {matched_account.get('account_id')}")
# 更新设备信息到服务器
device_name = serial # 使用设备序列号作为设备名称
# 构建更新数据
update_data = {
"account_id": matched_account.get("account_id"),
"device_name": device_name,
"device_port": port,
"device_ip": device_ip
}
success = update_device_info(
account_id=matched_account.get("account_id"),
device_name=device_name,
device_port=port,
device_ip=device_ip
)
if success:
print(f"🎉 所有操作完成! 账户 {matched_account.get('username')} 的设备信息已更新")
else:
print(f"⚠️ 设备信息更新失败但无线连接和App启动已完成")
# 关闭Appium连接
if driver:
print("🔄 关闭Appium连接...")
driver.quit()
break # 处理完第一个设备后退出,如需处理多个设备可移除此行
# =======================
# 程序入口
# =======================
if __name__ == "__main__":
# 配置参数
USER_ID = "68c0dbfdb7cbcd616e7c5ab5" # 替换为实际的用户ID
setup_adb_wireless(USER_ID)

View File

@@ -1,38 +0,0 @@
# -*- mode: python ; coding: utf-8 -*-
a = Analysis(
['create_a_link.py'],
pathex=[],
binaries=[],
datas=[],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='create_a_link',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)

Binary file not shown.

View File

@@ -378,6 +378,107 @@ def get_user_max_variation(username: str) -> Optional[int]:
return None return None
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
# def get_line_info_and_save_global(user_name: str) -> bool:
# """
# 调用get_name_all接口提取status=3的line_num和line_name存入全局字典
# :param user_name: 接口请求参数,如"wangshun"
# :return: 执行成功返回True失败/异常返回False
# """
# # 接口基础配置
# api_url = "https://engineering.yuxindazhineng.com/index/index/get_name_all"
# request_params = {"user_name": user_name} # GET请求参数
# timeout = 10 # 请求超时时间(秒),避免卡进程
# try:
# # 1. 发送GET请求
# response = requests.get(
# url=api_url,
# params=request_params, # GET参数用params传递自动拼接到URL后规范且防乱码
# timeout=timeout,
# verify=False # 禁用SSL验证适配HTTPS接口
# )
# # 2. 校验HTTP状态码先确保请求本身成功
# if response.status_code != 200:
# logging.error(f"接口请求失败HTTP状态码异常{response.status_code},响应内容:{response.text}")
# return False
# # 3. 解析JSON响应接口返回是JSON格式需解析为字典
# try:
# response_data = response.json()
# except Exception as e:
# logging.error(f"接口返回内容非合法JSON无法解析{response.text},错误:{str(e)}")
# return False
# # 4. 校验业务状态码接口约定code=0成功-1失败
# business_code = response_data.get("code")
# if business_code == 0:
# logging.info("接口业务请求成功,开始解析数据")
# elif business_code == -1:
# logging.error(f"接口业务请求失败业务状态码code=-1返回数据{response_data}")
# return False
# else:
# logging.warning(f"接口返回未知业务状态码:{business_code},请确认接口文档")
# return False
# # 5. 提取data字段校验数据是否存在
# api_data_list = response_data.get("data")
# if not api_data_list:
# logging.warning("接口业务成功但data字段为空或无数据")
# return False
# # 6. 校验data是否为列表类型
# if not isinstance(api_data_list, list):
# logging.error(f"data字段不是列表类型实际类型{type(api_data_list)},内容:{api_data_list}")
# return False
# found_valid_data = False
# # 7. 遍历列表提取所有status=3的数据
# for item in api_data_list:
# # 确保每个item是字典
# if not isinstance(item, dict):
# logging.warning(f"列表中的元素不是字典类型,跳过:{item}")
# continue
# # 获取字段值
# data_status = item.get("status")
# line_num = item.get("line_num")
# line_name = item.get("line_name")
# # 校验status是否为3且目标字段非空
# if data_status == 3 and line_num and line_name:
# # # 存入全局字典key=line_numvalue=line_name
# # global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT[line_num] = line_name
# # 存入全局字典key=line_namevalue=line_num
# global_variable.get_upload_breakpoint_dict()[line_name] = line_num
# print(f"当前全局字典数据上传线路字典数据:{global_variable.get_upload_breakpoint_dict()}")
# # 如果line_name不在列表中则添加
# if line_name not in global_variable.get_upload_breakpoint_list():
# global_variable.get_upload_breakpoint_list().append(line_name)
# logging.info(f"找到status=3的线路信息line_num={line_num}, line_name={line_name}")
# found_valid_data = True
# if found_valid_data:
# logging.info(f"成功提取所有status=3的线路信息当前全局字典数据{global_variable.get_upload_breakpoint_dict()}")
# return True
# else:
# logging.warning("data列表中未找到任何status=3且字段完整的线路信息")
# return False
# # 捕获所有请求相关异常(超时、连接失败、网络异常等)
# except requests.exceptions.Timeout:
# logging.error(f"调用get_name_all接口超时超时时间{timeout}秒,请求参数:{request_params}")
# return False
# except requests.exceptions.ConnectionError:
# logging.error(f"调用get_name_all接口连接失败检查网络或接口地址是否正确{api_url}")
# return False
# except Exception as e:
# logging.error(f"调用get_name_all接口时发生未知异常{str(e)}", exc_info=True) # exc_info=True打印异常堆栈方便排查
# return False
def get_line_info_and_save_global(user_name: str) -> bool: def get_line_info_and_save_global(user_name: str) -> bool:
""" """
调用get_name_all接口提取status=3的line_num和line_name存入全局字典 调用get_name_all接口提取status=3的line_num和line_name存入全局字典
@@ -388,7 +489,10 @@ def get_line_info_and_save_global(user_name: str) -> bool:
api_url = "https://engineering.yuxindazhineng.com/index/index/get_name_all" api_url = "https://engineering.yuxindazhineng.com/index/index/get_name_all"
request_params = {"user_name": user_name} # GET请求参数 request_params = {"user_name": user_name} # GET请求参数
timeout = 10 # 请求超时时间(秒),避免卡进程 timeout = 10 # 请求超时时间(秒),避免卡进程
max_retries = 3 # 最大重试次数
retry_interval = 2 # 重试间隔(秒)
for retry in range(max_retries):
try: try:
# 1. 发送GET请求 # 1. 发送GET请求
response = requests.get( response = requests.get(
@@ -401,6 +505,10 @@ def get_line_info_and_save_global(user_name: str) -> bool:
# 2. 校验HTTP状态码先确保请求本身成功 # 2. 校验HTTP状态码先确保请求本身成功
if response.status_code != 200: if response.status_code != 200:
logging.error(f"接口请求失败HTTP状态码异常{response.status_code},响应内容:{response.text}") logging.error(f"接口请求失败HTTP状态码异常{response.status_code},响应内容:{response.text}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
# 3. 解析JSON响应接口返回是JSON格式需解析为字典 # 3. 解析JSON响应接口返回是JSON格式需解析为字典
@@ -408,6 +516,10 @@ def get_line_info_and_save_global(user_name: str) -> bool:
response_data = response.json() response_data = response.json()
except Exception as e: except Exception as e:
logging.error(f"接口返回内容非合法JSON无法解析{response.text},错误:{str(e)}") logging.error(f"接口返回内容非合法JSON无法解析{response.text},错误:{str(e)}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
# 4. 校验业务状态码接口约定code=0成功-1失败 # 4. 校验业务状态码接口约定code=0成功-1失败
@@ -416,20 +528,36 @@ def get_line_info_and_save_global(user_name: str) -> bool:
logging.info("接口业务请求成功,开始解析数据") logging.info("接口业务请求成功,开始解析数据")
elif business_code == -1: elif business_code == -1:
logging.error(f"接口业务请求失败业务状态码code=-1返回数据{response_data}") logging.error(f"接口业务请求失败业务状态码code=-1返回数据{response_data}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
else: else:
logging.warning(f"接口返回未知业务状态码:{business_code},请确认接口文档") logging.warning(f"接口返回未知业务状态码:{business_code},请确认接口文档")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
# 5. 提取data字段校验数据是否存在 # 5. 提取data字段校验数据是否存在
api_data_list = response_data.get("data") api_data_list = response_data.get("data")
if not api_data_list: if not api_data_list:
logging.warning("接口业务成功但data字段为空或无数据") logging.warning("接口业务成功但data字段为空或无数据")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
# 6. 校验data是否为列表类型 # 6. 校验data是否为列表类型
if not isinstance(api_data_list, list): if not isinstance(api_data_list, list):
logging.error(f"data字段不是列表类型实际类型{type(api_data_list)},内容:{api_data_list}") logging.error(f"data字段不是列表类型实际类型{type(api_data_list)},内容:{api_data_list}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
found_valid_data = False found_valid_data = False
@@ -466,17 +594,33 @@ def get_line_info_and_save_global(user_name: str) -> bool:
return True return True
else: else:
logging.warning("data列表中未找到任何status=3且字段完整的线路信息") logging.warning("data列表中未找到任何status=3且字段完整的线路信息")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
# 捕获所有请求相关异常(超时、连接失败、网络异常等) # 捕获所有请求相关异常(超时、连接失败、网络异常等)
except requests.exceptions.Timeout: except requests.exceptions.Timeout:
logging.error(f"调用get_name_all接口超时超时时间{timeout}秒,请求参数:{request_params}") logging.error(f"调用get_name_all接口超时超时时间{timeout}秒,请求参数:{request_params}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
logging.error(f"调用get_name_all接口连接失败检查网络或接口地址是否正确{api_url}") logging.error(f"调用get_name_all接口连接失败检查网络或接口地址是否正确{api_url}")
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
except Exception as e: except Exception as e:
logging.error(f"调用get_name_all接口时发生未知异常{str(e)}", exc_info=True) # exc_info=True打印异常堆栈方便排查 logging.error(f"调用get_name_all接口时发生未知异常{str(e)}", exc_info=True) # exc_info=True打印异常堆栈方便排查
if retry < max_retries - 1:
logging.info(f"将在{retry_interval}秒后进行第{retry+2}次重试")
time.sleep(retry_interval)
continue
return False return False
def get_accounts_from_server(yh_id): def get_accounts_from_server(yh_id):

View File

@@ -126,6 +126,8 @@ def set_upload_success_breakpoint_list(success_list):
"""设置上传成功的断点列表""" """设置上传成功的断点列表"""
thread_local.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST = success_list thread_local.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST = success_list
# 为了保持 ,保留原有的全局变量名称 # 为了保持 ,保留原有的全局变量名称
# 但这些将不再被直接使用,而是通过上面的函数访问 # 但这些将不再被直接使用,而是通过上面的函数访问
GLOBAL_DEVICE_ID = "" # 设备ID GLOBAL_DEVICE_ID = "" # 设备ID

18
main.py
View File

@@ -16,9 +16,7 @@ import permissions # 导入权限处理模块
import globals.apis as apis import globals.apis as apis
from globals.driver_utils import init_appium_driver, ensure_appium_server_running, safe_quit_driver, is_app_launched, launch_app_manually from globals.driver_utils import init_appium_driver, ensure_appium_server_running, safe_quit_driver, is_app_launched, launch_app_manually
from page_objects.login_page import LoginPage from page_objects.login_page import LoginPage
from page_objects.download_tabbar_page import DownloadTabbarPage
from page_objects.screenshot_page import ScreenshotPage from page_objects.screenshot_page import ScreenshotPage
from page_objects.upload_config_page import UploadConfigPage
from page_objects.more_download_page import MoreDownloadPage from page_objects.more_download_page import MoreDownloadPage
@@ -35,8 +33,14 @@ logging.basicConfig(
class DeviceAutomation(object): class DeviceAutomation(object):
def __init__(self, device_id=None): def __init__(self, device_id=None, project_name=None):
self.device_id = device_id self.device_id = device_id
self.project_name = project_name
# 设置项目名称到全局变量
if project_name:
global_variable.set_current_project_name(project_name)
logging.info(f"设备 {self.device_id} 已设置项目名称: {project_name}")
# 初始化权限 # 初始化权限
if permissions.grant_appium_permissions(self.device_id): if permissions.grant_appium_permissions(self.device_id):
@@ -62,9 +66,7 @@ class DeviceAutomation(object):
# 初始化页面对象 # 初始化页面对象
logging.info(f"设备 {self.device_id} 开始初始化页面对象") logging.info(f"设备 {self.device_id} 开始初始化页面对象")
self.login_page = LoginPage(self.driver, self.wait) self.login_page = LoginPage(self.driver, self.wait)
self.download_tabbar_page = DownloadTabbarPage(self.driver, self.wait, self.device_id)
self.screenshot_page = ScreenshotPage(self.driver, self.wait, self.device_id) self.screenshot_page = ScreenshotPage(self.driver, self.wait, self.device_id)
self.upload_config_page = UploadConfigPage(self.driver, self.wait, self.device_id)
self.more_download_page = MoreDownloadPage(self.driver, self.wait,self.device_id) self.more_download_page = MoreDownloadPage(self.driver, self.wait,self.device_id)
logging.info(f"设备 {self.device_id} 所有页面对象初始化完成") logging.info(f"设备 {self.device_id} 所有页面对象初始化完成")
@@ -246,13 +248,15 @@ class DeviceAutomation(object):
safe_quit_driver(getattr(self, 'driver', None), self.device_id) safe_quit_driver(getattr(self, 'driver', None), self.device_id)
@staticmethod @staticmethod
def start_upload(device_id=None, upload_time=None): def start_upload(device_id=None, upload_time=None, project_name=None):
""" """
供其他页面或模块调用的静态方法 供其他页面或模块调用的静态方法
执行完整的自动化流程 执行完整的自动化流程
参数: 参数:
device_id: 可选的设备ID如果为None则自动获取 device_id: 可选的设备ID如果为None则自动获取
upload_time: 上传时间
project_name: 项目名称
返回: 返回:
bool: 自动化流程执行结果True/False bool: 自动化流程执行结果True/False
@@ -260,7 +264,7 @@ class DeviceAutomation(object):
automation = None automation = None
try: try:
# 创建自动化实例 # 创建自动化实例
automation = DeviceAutomation(device_id=device_id) automation = DeviceAutomation(device_id=device_id, project_name=project_name)
# 执行自动化流程 # 执行自动化流程
success = automation.run_automation() success = automation.run_automation()

View File

@@ -4,6 +4,7 @@ import logging
import time import time
import re import re
import os import os
import threading
from datetime import datetime from datetime import datetime
from appium.webdriver.common.appiumby import AppiumBy from appium.webdriver.common.appiumby import AppiumBy
from selenium.common.exceptions import NoSuchElementException, TimeoutException from selenium.common.exceptions import NoSuchElementException, TimeoutException
@@ -19,7 +20,7 @@ import globals.global_variable as global_variable # 导入全局变量模块
class ScreenshotPage: class ScreenshotPage:
def __init__(self, driver, wait, device_id=None): def __init__(self, driver, wait, device_id=None):
self.driver = driver self.driver = driver
self.wait = wait self.wait = WebDriverWait(driver, 2)
self.device_id = device_id self.device_id = device_id
self.logger = logging.getLogger(__name__) self.logger = logging.getLogger(__name__)
self.all_items = set() self.all_items = set()
@@ -528,7 +529,7 @@ class ScreenshotPage:
self.logger.error(f"设备 {device_id} 检查WiFi状态时发生错误: {str(e)}") self.logger.error(f"设备 {device_id} 检查WiFi状态时发生错误: {str(e)}")
return None return None
def take_screenshot(self, filename_prefix="screenshot"): def take_screenshot(self, filename_prefix="screenshot", date_str=None, time_str=None):
""" """
通过Appium驱动截取设备屏幕 通过Appium驱动截取设备屏幕
@@ -539,20 +540,58 @@ class ScreenshotPage:
bool: 操作是否成功 bool: 操作是否成功
""" """
try: try:
# 创建测试结果目录 # 获取项目名称
screenshots_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../test_results/screenshots') project_name = global_variable.get_current_project_name() or "默认项目"
if not os.path.exists(screenshots_dir):
os.makedirs(screenshots_dir) # 获取当前日期(如果没有提供)
self.logger.info(f"创建截图目录: {screenshots_dir}") if not date_str:
date_str = datetime.now().strftime("%Y%m%d")
# 获取当前时间(如果没有提供),并确保格式合法(不含冒号)
if not time_str:
time_str = datetime.now().strftime("%H%M%S")
else:
# 移除时间中的冒号,确保文件名合法
time_str = time_str.replace(":", "")
# 创建D盘下的截图目录结构D:\uploadInfo\picture\项目名\年月日
screenshots_dir = os.path.join("D:\\", "uploadInfo", "picture", project_name, date_str)
# 确保目录存在
try:
os.makedirs(screenshots_dir, exist_ok=True)
self.logger.info(f"截图目录: {screenshots_dir}")
except Exception as dir_error:
self.logger.error(f"创建截图目录失败: {str(dir_error)}")
return False
line_code = global_variable.get_upload_breakpoint_dict().get(filename_prefix)
if not line_code:
self.logger.error(f"未找到与断点名称 {filename_prefix} 对应的线路编码")
line_code = "unknown"
# 截图保存 # 截图保存
screenshot_file = os.path.join( screenshot_file = os.path.join(
screenshots_dir, screenshots_dir,
f"{filename_prefix}_{datetime.now().strftime('%Y%m%d')}.png" f"{line_code}_{filename_prefix}_{time_str}.png"
) )
self.driver.save_screenshot(screenshot_file)
# 尝试保存截图
try:
success = self.driver.save_screenshot(screenshot_file)
if success:
self.logger.info(f"截图已保存: {screenshot_file}") self.logger.info(f"截图已保存: {screenshot_file}")
# 验证文件是否真的存在
if os.path.exists(screenshot_file):
self.logger.info(f"截图文件验证存在: {screenshot_file}")
else:
self.logger.warning(f"截图文件保存成功但验证不存在: {screenshot_file}")
return True return True
else:
self.logger.error(f"Appium截图保存失败: {screenshot_file}")
return False
except Exception as save_error:
self.logger.error(f"保存截图时发生错误: {str(save_error)}")
return False
except Exception as e: except Exception as e:
self.logger.error(f"截图时发生错误: {str(e)}") self.logger.error(f"截图时发生错误: {str(e)}")
@@ -591,117 +630,79 @@ class ScreenshotPage:
self.logger.error(f"写入截图状态文件时发生错误: {str(e)}") self.logger.error(f"写入截图状态文件时发生错误: {str(e)}")
return False return False
def wait_for_measurement_end(self, timeout=900): def update_file_status(self, username, from_status, to_status):
""" """
等待按钮变成"测量结束"最多15分钟包含驱动重新初始化机制 安全地更新 time.txt 中该用户的状态
例如: 将 'true' 改为 'running', 或将 'running' 改为 'done'
Args:
timeout: 超时时间默认900秒15分钟
Returns:
bool: 是否成功等到测量结束按钮
""" """
try: TIME_FILE_PATH = r"D:\uploadInfo\time.txt"
# 更新WebDriverWait等待时间为900秒 if not os.path.exists(TIME_FILE_PATH):
self.wait = WebDriverWait(self.driver, 900)
self.logger.info(f"设备等待测量结束按钮出现,最多等待 {timeout}")
start_time = time.time()
reinit_attempts = 0
max_reinit_attempts = 3 # 最大重新初始化次数
while time.time() - start_time < timeout:
try:
# 使用XPath查找文本为"测量结束"的按钮
measurement_end_button = self.driver.find_element(
AppiumBy.XPATH,
"//android.widget.Button[@text='测量结束']"
)
if measurement_end_button.is_displayed() and measurement_end_button.is_enabled():
self.logger.info(f"设备检测到测量结束按钮")
return True
except NoSuchElementException:
# 按钮未找到,继续等待
pass
except Exception as e:
error_msg = str(e)
self.logger.warning(f"设备查找测量结束按钮时出现异常: {error_msg}")
# 检测是否是UiAutomator2服务崩溃
if 'UiAutomator2 server' in error_msg and 'instrumentation process is not running' in error_msg and reinit_attempts < max_reinit_attempts:
reinit_attempts += 1
self.logger.info(f"设备检测到UiAutomator2服务崩溃尝试第 {reinit_attempts} 次重新初始化驱动")
# 尝试重新初始化驱动
if self._reinit_driver():
self.logger.info(f"设备驱动重新初始化成功")
else:
self.logger.error(f"设备驱动重新初始化失败")
# 继续尝试,而不是立即失败
# 等待一段时间后再次检查
time.sleep(3)
# 每30秒输出一次等待状态
if int(time.time() - start_time) % 30 == 0:
elapsed = int(time.time() - start_time)
self.logger.info(f"设备 {self.device_id} 已等待 {elapsed} 秒,仍在等待测量结束...")
self.logger.error(f"设备 {self.device_id} 等待测量结束按钮超时")
return False return False
success = False
file_lock = threading.Lock()
with file_lock:
try:
with open(TIME_FILE_PATH, 'r', encoding='utf-8') as f:
lines = f.readlines()
# new_lines = []
# for line in lines:
# clean_line = line.strip()
# # 匹配逻辑:包含用户名 且 以 from_status 结尾
# if f" {username} " in line and clean_line.endswith(from_status):
# line = line.replace(from_status, to_status)
# success = True
# new_lines.append(line)
new_lines = []
for line in lines:
# 使用正则确保精准匹配用户名和结尾状态
# 匹配规则:行内包含该用户名,且该行以 from_status 结尾
if re.search(rf'\b{username}\b', line) and line.strip().endswith(from_status):
# 只替换行尾的那个状态词
line = re.sub(rf'{from_status}$', to_status, line.rstrip()) + '\n'
success = True
new_lines.append(line)
with open(TIME_FILE_PATH, 'w', encoding='utf-8') as f:
f.writelines(new_lines)
if success:
print(f"📝 [文件更新] 用户 {username}: {from_status} -> {to_status}")
return success
except Exception as e: except Exception as e:
self.logger.error(f"设备 {self.device_id} 等待测量结束时发生错误: {str(e)}") print(f"❌ 更新文件状态失败 ({username}): {e}")
return False return False
def _reinit_driver(self): def update_upload_info_status(self, status):
""" """
重新初始化Appium驱动 更新D:/uploadInfo文件夹下的time.txt文件的状态
Returns: 参数:
bool: 是否成功重新初始化 status: 状态值,如"ok""again"
返回:
bool: 操作是否成功
""" """
try: try:
# 首先尝试关闭现有的驱动 # time.txt文件路径
if hasattr(self, 'driver') and self.driver: time_file_path = "D:\\uploadInfo\\time.txt"
try:
self.driver.quit()
except:
self.logger.warning("关闭现有驱动时出现异常")
# 导入必要的模块 # 确保文件夹存在
from appium import webdriver os.makedirs(os.path.dirname(time_file_path), exist_ok=True)
from appium.options.android import UiAutomator2Options
# 重新创建驱动配置 # 写入状态
options = UiAutomator2Options() with open(time_file_path, 'w', encoding='utf-8') as f:
options.platform_name = "Android" f.write(status)
options.device_name = self.device_id
options.app_package = "com.bjjw.cjgc"
options.app_activity = ".activity.LoginActivity"
options.automation_name = "UiAutomator2"
options.no_reset = True
options.auto_grant_permissions = True
options.new_command_timeout = 300
options.udid = self.device_id
# 重新连接驱动 self.logger.info(f"已更新上传状态文件: {time_file_path} -> {status}")
self.logger.info(f"正在重新初始化设备 {self.device_id} 的驱动...")
self.driver = webdriver.Remote("http://localhost:4723", options=options)
# 重新初始化等待对象
from selenium.webdriver.support.ui import WebDriverWait
self.wait = WebDriverWait(self.driver, 1)
self.logger.info(f"设备 {self.device_id} 驱动重新初始化完成")
return True return True
except Exception as e: except Exception as e:
self.logger.error(f"设备 {self.device_id} 驱动重新初始化失败: {str(e)}") self.logger.error(f"更新上传状态文件时发生错误: {str(e)}")
return False return False
def handle_confirmation_dialog(self, device_id, timeout=2): def handle_confirmation_dialog(self, device_id, timeout=2):
""" """
处理确认弹窗,点击""按钮 处理确认弹窗,点击""按钮
@@ -808,7 +809,7 @@ class ScreenshotPage:
if confirm_button and confirm_button.is_displayed() and confirm_button.is_enabled(): if confirm_button and confirm_button.is_displayed() and confirm_button.is_enabled():
self.logger.info(f"设备 {device_id} 点击确认弹窗的''按钮") self.logger.info(f"设备 {device_id} 点击确认弹窗的''按钮")
confirm_button.click() confirm_button.click()
time.sleep(1) time.sleep(0.5)
# 验证弹窗是否消失 # 验证弹窗是否消失
try: try:
@@ -828,7 +829,7 @@ class ScreenshotPage:
except Exception as e: except Exception as e:
self.logger.warning(f"设备 {device_id} 查找确认弹窗时出现异常: {str(e)}") self.logger.warning(f"设备 {device_id} 查找确认弹窗时出现异常: {str(e)}")
time.sleep(1) time.sleep(0.5)
self.logger.error(f"设备 {device_id} 等待返回确认弹窗超时") self.logger.error(f"设备 {device_id} 等待返回确认弹窗超时")
return False return False
@@ -1001,7 +1002,7 @@ class ScreenshotPage:
# 3. 验证是否成功返回到上一页面 # 3. 验证是否成功返回到上一页面
time.sleep(1) # 等待页面跳转完成 time.sleep(0.5) # 等待页面跳转完成
# 可以添加页面验证逻辑,比如检查是否返回到预期的页面 # 可以添加页面验证逻辑,比如检查是否返回到预期的页面
# 这里可以根据实际应用添加特定的页面元素验证 # 这里可以根据实际应用添加特定的页面元素验证
@@ -1013,51 +1014,6 @@ class ScreenshotPage:
self.logger.error(f"设备 {device_id} 执行返回导航步骤时发生错误: {str(e)}") self.logger.error(f"设备 {device_id} 执行返回导航步骤时发生错误: {str(e)}")
return False return False
def scroll_to_bottom_and_screenshot(self, device_id):
"""
检测到测量结束后下滑列表到最底端点击最后一个spinner再下滑一次点击平差处理按钮后截图
Args:
device_id: 设备ID
Returns:
bool: 操作是否成功
"""
try:
self.logger.info(f"设备 {device_id} 开始执行测量结束后的操作流程")
time.sleep(5)
# 1. 下滑列表到最底端
if not self.scroll_list_to_bottom(device_id):
self.logger.error(f"设备 {device_id} 下滑列表到底部失败")
return False
# 2. 点击最后一个spinner
if not self.click_last_spinner_with_retry(device_id):
self.logger.error(f"设备 {device_id} 点击最后一个spinner失败")
return False
# 3. 再下滑一次
if not self.scroll_down_once(device_id):
self.logger.warning(f"设备 {device_id} 再次下滑失败,但继续执行")
# 4. 点击平差处理按钮
if not self.click_adjustment_button(device_id):
self.logger.error(f"设备 {device_id} 点击平差处理按钮失败")
return False
# 5. 在点击平差处理按钮后截图
time.sleep(2) # 等待平差处理按钮点击后的界面变化
if not self.take_screenshot("after_adjustment_button_click"):
self.logger.error(f"设备 {device_id} 截图失败")
return False
self.logger.info(f"设备 {device_id} 测量结束后操作流程完成")
return True
except Exception as e:
self.logger.error(f"设备 {device_id} 执行测量结束后操作时发生错误: {str(e)}")
return False
def scroll_list_to_bottom(self, device_id, max_swipes=60): def scroll_list_to_bottom(self, device_id, max_swipes=60):
""" """
@@ -1094,7 +1050,7 @@ class ScreenshotPage:
'duration': 500 'duration': 500
}) })
time.sleep(0.5) # time.sleep(0.5)
# 获取滑动后的子元素文本 # 获取滑动后的子元素文本
new_child_elements = list_view.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView") new_child_elements = list_view.find_elements(AppiumBy.CLASS_NAME, "android.widget.TextView")
@@ -1129,7 +1085,7 @@ class ScreenshotPage:
if self.click_last_spinner(device_id): if self.click_last_spinner(device_id):
return True return True
self.logger.warning(f"设备 {device_id}{attempt + 1}次点击失败,准备重试") self.logger.warning(f"设备 {device_id}{attempt + 1}次点击失败,准备重试")
time.sleep(1) # 重试前等待 time.sleep(0.5) # 重试前等待
except Exception as e: except Exception as e:
self.logger.error(f"设备 {device_id}{attempt + 1}次尝试失败: {str(e)}") self.logger.error(f"设备 {device_id}{attempt + 1}次尝试失败: {str(e)}")
@@ -1280,7 +1236,7 @@ class ScreenshotPage:
'percent': 0.5 'percent': 0.5
}) })
time.sleep(1) time.sleep(0.2)
self.logger.info(f"设备 {device_id} 额外下滑完成") self.logger.info(f"设备 {device_id} 额外下滑完成")
return True return True
@@ -1380,7 +1336,7 @@ class ScreenshotPage:
self.logger.info(f"设备 {device_id} 等待 {timeout} 秒未发现确认弹窗,可能没有弹窗,返回成功") self.logger.info(f"设备 {device_id} 等待 {timeout} 秒未发现确认弹窗,可能没有弹窗,返回成功")
return True return True
def load_line_time_mapping_dict(self, filename="20251022.1.CZSCZQ-3fhg0410.txt", log_directory="D:\\soft\\安卓时间修改-v0.7.13-1\\Logs\\202510", poll_interval=120, max_wait_time=18000): def load_line_time_mapping_dict(self, filename="20251022.1.CZSCZQ-3fhg0410.txt", log_directory="D:\\uploadInfo\\Logs\\202510", poll_interval=120, max_wait_time=18000):
""" """
加载指定文件中的线路编码和时间到全局字典 加载指定文件中的线路编码和时间到全局字典
参数: 参数:
@@ -1466,11 +1422,11 @@ class ScreenshotPage:
"""执行截图页面管理操作""" """执行截图页面管理操作"""
try: try:
# 加载指定文件中的线路编码和时间到全局字典 # 加载指定文件中的线路编码和时间到全局字典
if not self.load_line_time_mapping_dict("20251022.1.CZSCZQ-3fhg0410.txt", "D:\\soft\\安卓时间修改-v0.7.13-1\\Logs\\202510"): if not self.load_line_time_mapping_dict("20251022.1.CZSCZQ-3fhg0410.txt", "D:\\uploadInfo\\Logs\\202510"):
self.logger.error(f"设备 {device_id} 加载线路时间映射字典失败") self.logger.error(f"设备 {device_id} 加载线路时间映射字典失败")
return False return False
time.sleep(5) # time.sleep(5)
# 循环检查数据数量是否一致,直到获取到完整数据 # 循环检查数据数量是否一致,直到获取到完整数据
retry_count = 0 retry_count = 0
@@ -1496,7 +1452,7 @@ class ScreenshotPage:
time.sleep(60) # 等待3分钟 time.sleep(60) # 等待3分钟
# 重新加载文件 # 重新加载文件
if not self.load_line_time_mapping_dict("20251022.1.CZSCZQ-3fhg0410.txt", "D:\\soft\\安卓时间修改-v0.7.13-1\\Logs\\202510"): if not self.load_line_time_mapping_dict("20251022.1.CZSCZQ-3fhg0410.txt", "D:\\uploadInfo\\Logs\\202510"):
self.logger.error(f"设备 {device_id} 重新加载线路时间映射字典失败") self.logger.error(f"设备 {device_id} 重新加载线路时间映射字典失败")
else: else:
self.logger.info(f"设备 {device_id} 重新加载完成,新的线路时间数量: {len(global_variable.get_line_time_mapping_dict())}") self.logger.info(f"设备 {device_id} 重新加载完成,新的线路时间数量: {len(global_variable.get_line_time_mapping_dict())}")
@@ -1524,98 +1480,90 @@ class ScreenshotPage:
}) })
# 开始循环 # 开始循环
all_success = True
for breakpoint_name in global_variable.get_upload_breakpoint_dict().keys(): for breakpoint_name in global_variable.get_upload_breakpoint_dict().keys():
self.logger.info(f"开始处理要平差截图的断点 {breakpoint_name}") self.logger.info(f"开始处理要平差截图的断点 {breakpoint_name}")
# 把断点名称给find_keyword # 把断点名称给find_keyword
if not self.find_keyword(breakpoint_name): if not self.find_keyword(breakpoint_name):
self.logger.error(f"设备 {device_id} 未找到包含 {breakpoint_name} 的文件名") self.logger.error(f"设备 {device_id} 未找到包含 {breakpoint_name} 的文件名")
return False all_success = False
continue
if not self.handle_measurement_dialog(): if not self.handle_measurement_dialog():
self.logger.error(f"设备 {device_id} 处理测量弹窗失败") self.logger.error(f"设备 {device_id} 处理测量弹窗失败")
return False all_success = False
continue
if not self.check_apply_btn(): if not self.check_apply_btn():
self.logger.error(f"设备 {device_id} 检查平差处理按钮失败") self.logger.error(f"设备 {device_id} 检查平差处理按钮失败")
return False all_success = False
self.click_back_button(device_id)
continue
# 根据断点名称在get_upload_breakpoint_dict()中获取线路编码 # 根据断点名称在get_upload_breakpoint_dict()中获取线路编码
line_code = global_variable.get_upload_breakpoint_dict().get(breakpoint_name) line_code = global_variable.get_upload_breakpoint_dict().get(breakpoint_name)
if not line_code: if not line_code:
self.logger.error(f"设备 {device_id} 未找到断点 {breakpoint_name} 对应的线路编码") self.logger.error(f"设备 {device_id} 未找到断点 {breakpoint_name} 对应的线路编码")
return False all_success = False
continue
# 根据线路编码查找对应的时间 # 根据线路编码查找对应的时间
date_str, time_str = self.get_line_end_time(line_code) date_str, time_str = self.get_line_end_time(line_code)
if not time_str or not date_str: if not time_str or not date_str:
self.logger.error(f"设备 {device_id} 未找到线路 {line_code} 对应的时间") self.logger.error(f"设备 {device_id} 未找到线路 {line_code} 对应的时间")
return False all_success = False
continue
# 修改时间 # 修改时间
if not self.set_device_time(device_id, time_str, date_str): if not self.set_device_time(device_id, time_str, date_str):
self.logger.error(f"设备 {device_id} 设置设备时间失败") self.logger.error(f"设备 {device_id} 设置设备时间失败")
return False all_success = False
continue
# 滑动列表到底部 # 滑动列表到底部
if not self.scroll_list_to_bottom(device_id): if not self.scroll_list_to_bottom(device_id):
self.logger.error(f"设备 {device_id} 下滑列表到底部失败") self.logger.error(f"设备 {device_id} 下滑列表到底部失败")
return False all_success = False
continue
# 2. 点击最后一个spinner # 2. 点击最后一个spinner
if not self.click_last_spinner_with_retry(device_id): if not self.click_last_spinner_with_retry(device_id):
self.logger.error(f"设备 {device_id} 点击最后一个spinner失败") self.logger.error(f"设备 {device_id} 点击最后一个spinner失败")
return False all_success = False
continue
# 3. 再下滑一次 # 3. 再下滑一次
if not self.scroll_down_once(device_id): if not self.scroll_down_once(device_id):
self.logger.warning(f"设备 {device_id} 再次下滑失败,但继续执行") self.logger.warning(f"设备 {device_id} 再次下滑失败,但继续执行")
# # 4. 点击平差处理按钮
# if not self.click_adjustment_button(device_id):
# self.logger.error(f"设备 {device_id} 点击平差处理按钮失败")
# return False
# # 检查是否在测量页面在就重新执行选择断点滑动列表到底部点击最后一个spinner 再下滑一次,点击平差处理按钮平差
# if not self.handle_back_navigation(breakpoint_name, device_id):
# self.logger.error(f"{breakpoint_name}平差失败,未截图")
# return False
# # 检测并处理"是 保留成果"弹窗
# if not self.handle_adjustment_result_dialog():
# self.logger.error("处理平差结果弹窗失败")
# return False
# # 平差完成,将断点数据保存到上传列表中
# if not self.add_breakpoint_to_upload_list(breakpoint_name, line_code):
# self.logger.error(f"设备 {device_id} 保存断点 {breakpoint_name} 到上传列表失败")
# return False
# # 禁用WiFi
# if not self.disable_wifi(device_id):
# self.logger.error(f"设备 {device_id} 禁用WiFi失败")
# return False
# 平差处理完成后截图 # 平差处理完成后截图
time.sleep(3) # 等待平差处理按钮点击后的界面变化 time.sleep(0.2) # 等待平差处理按钮点击后的界面变化
logging.info("断点保存到上传列表成功,开始截图") logging.info("断点保存到上传列表成功,开始截图")
if not self.take_screenshot(breakpoint_name): # png_time = date_str + " " + time_str
if not self.take_screenshot(breakpoint_name, date_str, time_str):
self.logger.error(f"设备 {device_id} 截图失败") self.logger.error(f"设备 {device_id} 截图失败")
self.write_screenshot_status(breakpoint_name, success=False) self.write_screenshot_status(breakpoint_name, success=False)
return False all_success = False
else:
self.write_screenshot_status(breakpoint_name, success=True) self.write_screenshot_status(breakpoint_name, success=True)
# 点击返回按钮并处理弹窗 # 点击返回按钮并处理弹窗
if not self.execute_back_navigation_steps(device_id): if not self.execute_back_navigation_steps(device_id):
self.logger.error(f"设备 {device_id} 处理返回按钮确认失败") self.logger.error(f"设备 {device_id} 处理返回按钮确认失败")
return False all_success = False
# 启用WiFi # 启用WiFi
# if not self.enable_wifi(device_id): # if not self.enable_wifi(device_id):
# self.logger.error(f"设备 {device_id} 启用WiFi失败") # self.logger.error(f"设备 {device_id} 启用WiFi失败")
# return False # return False
self.logger.info(f"设备 {device_id} 截图页面操作执行完成") # 根据截图结果更新time.txt文件状态
status = "ok" if all_success else "true"
# self.update_upload_info_status(status)
username = global_variable.get_username()
self.update_file_status(username, "running", status)
self.logger.info(f"{username} 截图完成状态为 {status}")
self.logger.info(f"设备 {device_id} 截图页面操作执行完成,状态: {status}")
return True return True
except Exception as e: except Exception as e:
self.logger.error(f"设备 {device_id} 执行截图页面操作时出错: {str(e)}") self.logger.error(f"设备 {device_id} 执行截图页面操作时出错: {str(e)}")

View File

@@ -73,7 +73,7 @@ def parse_time_config():
for line in f: for line in f:
line = line.strip() line = line.strip()
# 匹配:用户名 时间 true (仅获取待处理任务) # 匹配:用户名 时间 true (仅获取待处理任务)
match = re.search(r'(\w+)\s+(\d{1,2}:\d{2}:\d{2})\s+true$', line) match = re.search(r'(\w+)\s+(\d{4}-\d{1,2}-\d{1,2}\s+\d{1,2}:\d{2}:\d{2})\s+true$', line)
if match: if match:
username, scheduled_time = match.group(1), match.group(2) username, scheduled_time = match.group(1), match.group(2)
time_map[username] = scheduled_time time_map[username] = scheduled_time
@@ -81,6 +81,37 @@ def parse_time_config():
print(f"❌ 解析 time.txt 失败: {e}") print(f"❌ 解析 time.txt 失败: {e}")
return time_map return time_map
def normalize_datetime(time_str):
"""
将时间字符串格式化为标准格式YYYY-MM-DD HH:MM:SS
补全单数字的月、日、时
例如2024-1-15 9:52:20 -> 2024-01-15 09:52:20
"""
try:
# 分割日期和时间部分
if ' ' in time_str:
date_part, time_part = time_str.split(' ', 1)
# 补全日期部分的单数字
date_parts = date_part.split('-')
if len(date_parts) == 3:
year = date_parts[0]
month = date_parts[1].zfill(2) # 月补零
day = date_parts[2].zfill(2) # 日补零
date_part = f"{year}-{month}-{day}"
# 补全时间部分的单数字小时
time_parts = time_part.split(':')
if len(time_parts) >= 1:
hour = time_parts[0].zfill(2) # 小时补零
time_part = f"{hour}:{':'.join(time_parts[1:])}"
return f"{date_part} {time_part}"
return time_str
except Exception as e:
print(f"⚠️ 时间格式标准化失败 ({time_str}): {e}")
return time_str
def get_combined_tasks(): def get_combined_tasks():
""" """
结合接口(is_ok==1)和本地文件(true)筛选任务 结合接口(is_ok==1)和本地文件(true)筛选任务
@@ -96,13 +127,14 @@ def get_combined_tasks():
return {} return {}
task_list = {} task_list = {}
today = datetime.now().strftime("%Y-%m-%d") # today = datetime.now().strftime("%Y-%m-%d")
for account in accounts: for account in accounts:
if account.get('is_ok') == 1 or account.get('username') == "czyuzongwen": if account.get('is_ok') == 1:
user = account.get('username') user = account.get('username')
ip = account.get('device_ip') ip = account.get('device_ip')
port = account.get('device_port') port = account.get('device_port')
project_name = account.get('project_name')
# 只有在 time.txt 中是 true 的账号才会被加入 # 只有在 time.txt 中是 true 的账号才会被加入
if user in local_times and ip and port: if user in local_times and ip and port:
@@ -110,21 +142,22 @@ def get_combined_tasks():
# full_time = f"{today} {local_times[user]}" # full_time = f"{today} {local_times[user]}"
# 确保时间是两位数格式 # 确保时间是两位数格式
raw_time = local_times[user] raw_time = local_times[user]
# 将时间格式化为两位数9:52:20 -> 09:52:20 # # 将时间格式化为两位数9:52:20 -> 09:52:20
if ':' in raw_time: # if ':' in raw_time:
parts = raw_time.split(':') # parts = raw_time.split(':')
if len(parts[0]) == 1: # if len(parts[0]) == 1:
raw_time = f"0{raw_time}" # 补齐前导零 # raw_time = f"0{raw_time}" # 补齐前导零
full_time = f"{today} {raw_time}" # full_time = f"{today} {raw_time}"
task_list[address] = {"time": full_time, "user": user} full_time = normalize_datetime(raw_time)
task_list[address] = {"time": full_time, "user": user, "project_name": project_name}
return task_list return task_list
except Exception as e: except Exception as e:
print(f"❌ 获取任务异常: {e}") print(f"❌ 获取任务异常: {e}")
return {} return {}
def run_task(address, target_time, username): def run_task(address, target_time, username, project_name):
""" """
单个执行线程:锁定状态 -> 等待 -> 执行 -> 完成 单个执行线程:锁定状态 -> 等待 -> 执行 -> 完成
""" """
@@ -133,7 +166,7 @@ def run_task(address, target_time, username):
if not update_file_status(username, "true", "running"): if not update_file_status(username, "true", "running"):
return f"⏭️ {username} 状态已变更,跳过执行。" return f"⏭️ {username} 状态已变更,跳过执行。"
print(f"🚀 [任务锁定] 设备: {address} | 用户: {username} | 计划时间: {target_time}") print(f"🚀 [任务锁定] 设备: {address} | 用户: {username} | 计划时间: {target_time} | 项目: {project_name}")
try: try:
# 2. 计算并执行等待逻辑 # 2. 计算并执行等待逻辑
@@ -146,7 +179,7 @@ def run_task(address, target_time, username):
# 3. 调用 main.py 中的自动化逻辑 # 3. 调用 main.py 中的自动化逻辑
print(f"▶️ [正在执行] {username} 开始自动化操作...") print(f"▶️ [正在执行] {username} 开始自动化操作...")
automation = DeviceAutomation(address) automation = DeviceAutomation(address, project_name)
result = automation.handle_app_state() result = automation.handle_app_state()
# 4. 执行完成后,将状态从 running 改为 done # 4. 执行完成后,将状态从 running 改为 done
@@ -169,9 +202,9 @@ def monitor_center():
if tasks: if tasks:
print(f"📡 发现 {len(tasks)} 个符合条件且未跑过的任务,准备启动线程池...") print(f"📡 发现 {len(tasks)} 个符合条件且未跑过的任务,准备启动线程池...")
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor: with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
# 提交任务,将 address, time, username 传入 # 提交任务,将 address, time, username, project_name 传入
future_to_user = { future_to_user = {
executor.submit(run_task, addr, info['time'], info['user']): info['user'] executor.submit(run_task, addr, info['time'], info['user'], info.get('project_name', '')): info['user']
for addr, info in tasks.items() for addr, info in tasks.items()
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

View File

@@ -22,7 +22,7 @@ exe = EXE(
a.binaries, a.binaries,
a.datas, a.datas,
[], [],
name='沉降观测自动上传', name='沉降观测自动截图',
debug=False, debug=False,
bootloader_ignore_signals=False, bootloader_ignore_signals=False,
strip=False, strip=False,