修改上传人员信息表为D盘固定位置;新增上传弹窗检查

This commit is contained in:
2026-02-09 08:53:41 +08:00
parent 5c38228a1c
commit ac4d41c70b
30 changed files with 111274 additions and 883 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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,347 @@
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), http.server (delayed, optional), psutil (optional), setuptools._distutils.util (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (optional)
missing module named grp - imported by shutil (delayed, optional), tarfile (optional), pathlib (delayed, optional), subprocess (delayed, conditional, optional), setuptools._distutils.archive_util (optional), setuptools._vendor.backports.tarfile (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), tty (top-level)
missing module named _sha512 - imported by random (optional)
missing module named urllib.urlopen - imported by urllib (delayed, optional), lxml.html (delayed, optional)
missing module named urllib.urlencode - imported by urllib (delayed, optional), lxml.html (delayed, 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 asyncio.DefaultEventLoopPolicy - imported by asyncio (delayed, conditional), asyncio.events (delayed, conditional)
missing module named annotationlib - imported by typing_extensions (conditional)
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)
missing module named _winreg - imported by platform (delayed, optional), selenium.webdriver.firefox.firefox_binary (delayed, optional)
missing module named usercustomize - imported by site (delayed, optional)
missing module named sitecustomize - imported by site (delayed, optional)
missing module named readline - imported by cmd (delayed, conditional, optional), code (delayed, conditional, optional), pdb (delayed, optional), site (delayed, optional), rlcompleter (optional)
missing module named _typeshed - imported by setuptools._distutils.dist (conditional), setuptools.glob (conditional), setuptools.compat.py311 (conditional), pkg_resources (conditional)
missing module named '_typeshed.importlib' - imported by pkg_resources (conditional)
missing module named jnius - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
missing module named android - imported by setuptools._vendor.platformdirs.android (delayed, conditional, optional)
missing module named importlib_resources - imported by setuptools._vendor.jaraco.text (optional)
missing module named jaraco.text.yield_lines - imported by setuptools._vendor.jaraco.text (top-level), setuptools._entry_points (top-level), setuptools.command._requirestxt (top-level)
missing module named _manylinux - imported by packaging._manylinux (delayed, optional), setuptools._vendor.packaging._manylinux (delayed, optional), setuptools._vendor.wheel.vendored.packaging._manylinux (delayed, optional)
missing module named trove_classifiers - imported by setuptools.config._validate_pyproject.formats (optional)
missing module named pyimod02_importers - imported by C:\Program Files\Python312\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgutil.py (delayed), C:\Program Files\Python312\Lib\site-packages\PyInstaller\hooks\rthooks\pyi_rth_pkgres.py (delayed)
missing module named collections.Mapping - imported by collections (optional), pytz.lazy (optional)
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 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 six.moves.range - imported by six.moves (top-level), dateutil.rrule (top-level)
runtime module named six.moves - imported by dateutil.tz.tz (top-level), dateutil.tz._factories (top-level), dateutil.tz.win (top-level), dateutil.rrule (top-level)
missing module named dateutil.tz.tzfile - imported by dateutil.tz (top-level), dateutil.zoneinfo (top-level)
missing module named StringIO - imported by six (conditional)
missing module named numexpr - imported by pandas.core.computation.expressions (conditional), pandas.core.computation.engines (delayed)
missing module named numba - imported by pandas.core._numba.executor (delayed, conditional), pandas.core.util.numba_ (delayed, conditional), pandas.core.window.numba_ (delayed, conditional), pandas.core.window.online (delayed, conditional), pandas.core._numba.kernels.mean_ (top-level), pandas.core._numba.kernels.shared (top-level), pandas.core._numba.kernels.sum_ (top-level), pandas.core._numba.kernels.min_max_ (top-level), pandas.core._numba.kernels.var_ (top-level), pandas.core.groupby.numba_ (delayed, conditional), pandas.core._numba.extensions (top-level)
missing module named 'numba.extending' - imported by pandas.core._numba.kernels.sum_ (top-level)
missing module named 'pyarrow.compute' - imported by pandas.core.arrays.arrow.accessors (conditional), pandas.core.arrays._arrow_string_mixins (conditional), pandas.core.arrays.string_arrow (conditional), pandas.core.reshape.merge (delayed, conditional), pandas.core.arrays.arrow.array (conditional)
missing module named 'numba.typed' - imported by pandas.core._numba.extensions (delayed)
missing module named 'numba.core' - imported by pandas.core._numba.extensions (top-level)
missing module named pyarrow - imported by pandas.core.arrays.arrow.accessors (conditional), pandas.core.arrays.masked (delayed), pandas.core.arrays.boolean (delayed, conditional), pandas.core.arrays.string_ (delayed, conditional), pandas.core.arrays._arrow_string_mixins (conditional), pandas.core.arrays.string_arrow (conditional), pandas.core.arrays.arrow._arrow_utils (top-level), pandas.core.interchange.utils (delayed, conditional), pandas.core.strings.accessor (delayed, conditional), pandas.io.parsers.base_parser (delayed, conditional), pandas.core.arrays.interval (delayed), pandas.core.arrays.arrow.extension_types (top-level), pandas.core.arrays.period (delayed), pandas.core.methods.describe (delayed, conditional), pandas.io.sql (delayed, conditional), pandas.core.reshape.merge (delayed, conditional), pandas.core.arrays.numeric (delayed, conditional), pandas.core.interchange.buffer (conditional), pandas.io.feather_format (delayed), pandas.core.indexes.base (delayed, conditional), pandas.core.dtypes.cast (delayed, conditional), pandas.core.arrays.arrow.array (conditional), pandas.core.dtypes.dtypes (delayed, conditional), pandas.compat.pyarrow (optional), pandas.core.reshape.encoding (delayed, conditional), pandas._testing (conditional)
missing module named _dummy_thread - imported by numpy._core.arrayprint (optional)
missing module named numpy._core.void - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.vecmat - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ushort - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.unsignedinteger - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulonglong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ulong - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uintc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.uint64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.uint - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ubyte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.trunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.true_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.timedelta64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.tan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.subtract - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.str_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.square - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.spacing - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.sinh - 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.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.right_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.remainder - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.radians - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.rad2deg - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.positive - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.pi - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.not_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.negative - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.modf - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.mod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.minimum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.maximum - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.matvec - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.longdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.long - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_not - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logical_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.logaddexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log1p - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.log - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.less_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.less - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.left_shift - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ldexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.lcm - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.integer - imported by numpy._core (conditional), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.int8 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int32 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.int16 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.hypot - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.heaviside - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.half - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater_equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.greater - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.gcd - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frompyfunc - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.frexp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fmax - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor_divide - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.floating - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float_power - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.float16 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.fabs - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.expm1 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.exp - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.euler_gamma - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.equal - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.e - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.divmod - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.degrees - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.deg2rad - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.datetime64 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cos - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.copysign - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.conjugate - imported by numpy._core (conditional), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.conj - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.complex64 - imported by numpy._core (conditional), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.clongdouble - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.character - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ceil - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.cbrt - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bytes_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.byte - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bool_ - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_xor - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_or - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_count - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.bitwise_and - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctanh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan2 - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arctan - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsinh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arcsin - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccosh - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.arccos - imported by numpy._core (conditional), numpy (conditional)
missing module named numpy._core.ones - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.hstack - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.atleast_1d - imported by numpy._core (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.atleast_3d - 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.linspace - imported by numpy._core (top-level), numpy.lib._index_tricks_impl (top-level), numpy (conditional)
missing module named numpy._core.result_type - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.number - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.max - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.array2string - 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.isscalar - imported by numpy._core (delayed), numpy.testing._private.utils (delayed), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.isnat - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.array_repr - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.arange - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.float32 - imported by numpy._core (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.vecdot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.matrix_transpose - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.matmul - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.tensordot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.outer - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cross - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.trace - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.diagonal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.reciprocal - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.sort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.argsort - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), 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.isnan - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.count_nonzero - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.divide - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.swapaxes - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.object_ - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.asanyarray - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.intp - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy._array_api_info (top-level)
missing module named numpy._core.atleast_2d - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.prod - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.amax - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.amin - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.moveaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.errstate - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.finfo - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.isfinite - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sum - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.sqrt - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.multiply - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.add - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.dot - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.inf - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.all - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (delayed), numpy (conditional)
missing module named numpy._core.newaxis - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.complexfloating - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.inexact - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.cdouble - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.csingle - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.double - 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.intc - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.empty_like - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level)
missing module named numpy._core.empty - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy (conditional), numpy.fft._helper (top-level)
missing module named numpy._core.zeros - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.array - imported by numpy._core (top-level), numpy.linalg._linalg (top-level), numpy.testing._private.utils (top-level), numpy.lib._polynomial_impl (top-level), numpy (conditional)
missing module named numpy._core.iinfo - imported by numpy._core (top-level), numpy.lib._twodim_base_impl (top-level), numpy (conditional)
missing module named numpy._core.transpose - imported by numpy._core (top-level), numpy.lib._function_base_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional)
missing module named numpy._core.ndarray - imported by numpy._core (top-level), numpy.lib._utils_impl (top-level), numpy.testing._private.utils (top-level), numpy (conditional)
missing module named numpy._core.asarray - imported by numpy._core (top-level), numpy.lib._array_utils_impl (top-level), numpy.linalg._linalg (top-level), numpy (conditional), numpy.fft._pocketfft (top-level), numpy.fft._helper (top-level)
missing module named threadpoolctl - imported by numpy.lib._utils_impl (delayed, optional)
missing module named 'scipy.stats' - imported by pandas.core.nanops (delayed, conditional), pandas.plotting._matplotlib.misc (delayed, conditional), pandas.plotting._matplotlib.hist (delayed)
missing module named scipy - imported by pandas.core.nanops (delayed, conditional), pandas.core.missing (delayed)
missing module named traitlets - imported by pandas.io.formats.printing (delayed, conditional)
missing module named 'IPython.core' - imported by pandas.io.formats.printing (delayed, conditional)
missing module named IPython - imported by pandas.io.formats.printing (delayed)
missing module named jinja2 - imported by pyparsing.diagram (top-level), pandas.io.formats.style (top-level)
missing module named defusedxml - imported by PIL.Image (optional), openpyxl.xml (delayed, optional)
missing module named 'defusedxml.ElementTree' - imported by openpyxl.xml.functions (conditional)
missing module named htmlentitydefs - imported by lxml.html.soupparser (optional)
missing module named BeautifulSoup - imported by lxml.html.soupparser (optional)
missing module named cchardet - imported by bs4.dammit (optional)
missing module named 'html5lib.treebuilders' - imported by bs4.builder._html5lib (top-level), lxml.html._html5builder (top-level), lxml.html.html5parser (top-level)
missing module named 'html5lib.constants' - imported by bs4.builder._html5lib (top-level)
missing module named html5lib - imported by bs4.builder._html5lib (top-level), lxml.html.html5parser (top-level)
missing module named urlparse - imported by lxml.ElementInclude (optional), lxml.html.html5parser (optional)
missing module named urllib2 - imported by lxml.ElementInclude (optional), lxml.html.html5parser (optional)
missing module named lxml_html_clean - imported by lxml.html.clean (optional)
missing module named cssselect - imported by lxml.cssselect (optional)
missing module named olefile - imported by PIL.FpxImagePlugin (top-level), PIL.MicImagePlugin (top-level)
missing module named openpyxl.tests - imported by openpyxl.reader.excel (optional)
missing module named 'odf.config' - imported by pandas.io.excel._odswriter (delayed)
missing module named 'odf.style' - imported by pandas.io.excel._odswriter (delayed)
missing module named 'odf.text' - imported by pandas.io.excel._odfreader (delayed), pandas.io.excel._odswriter (delayed)
missing module named 'odf.table' - imported by pandas.io.excel._odfreader (delayed), pandas.io.excel._odswriter (delayed)
missing module named 'odf.opendocument' - imported by pandas.io.excel._odfreader (delayed), pandas.io.excel._odswriter (delayed)
missing module named cStringIO - imported by xlrd.timemachine (conditional)
missing module named pyxlsb - imported by pandas.io.excel._pyxlsb (delayed, conditional)
missing module named 'odf.office' - imported by pandas.io.excel._odfreader (delayed)
missing module named 'odf.element' - imported by pandas.io.excel._odfreader (delayed)
missing module named 'odf.namespaces' - imported by pandas.io.excel._odfreader (delayed)
missing module named odf - imported by pandas.io.excel._odfreader (conditional)
missing module named python_calamine - imported by pandas.io.excel._calamine (delayed, conditional)
missing module named railroad - imported by pyparsing.diagram (top-level)
missing module named pyparsing.Word - imported by pyparsing (delayed), pyparsing.unicode (delayed)
missing module named 'tornado.template' - imported by matplotlib.backends.backend_webagg (delayed)
missing module named 'tornado.websocket' - imported by matplotlib.backends.backend_webagg (top-level)
missing module named 'tornado.ioloop' - imported by matplotlib.backends.backend_webagg (top-level)
missing module named 'tornado.web' - imported by matplotlib.backends.backend_webagg (top-level)
missing module named tornado - imported by matplotlib.backends.backend_webagg (optional), matplotlib.backends.backend_webagg_core (delayed)
missing module named gi - imported by matplotlib.cbook (delayed, conditional)
missing module named numpy.VisibleDeprecationWarning - imported by numpy (optional), matplotlib.cbook (optional)
missing module named setuptools_scm - imported by matplotlib (delayed, conditional, optional)
missing module named markupsafe - imported by pandas.io.formats.style_render (top-level)
missing module named botocore - imported by pandas.io.common (delayed, conditional, optional)
missing module named sets - imported by pytz.tzinfo (optional)
missing module named UserDict - imported by pytz.lazy (optional)
missing module named 'scipy.sparse' - imported by pandas.core.arrays.sparse.array (conditional), pandas.core.arrays.sparse.scipy_sparse (delayed, conditional), pandas.core.arrays.sparse.accessor (delayed), pandas.core.dtypes.common (delayed, conditional, optional)
missing module named pandas.core.internals.Block - imported by pandas.core.internals (conditional), pandas.io.pytables (conditional)
missing module named Foundation - imported by pandas.io.clipboard (delayed, conditional, optional)
missing module named AppKit - imported by pandas.io.clipboard (delayed, conditional, optional)
missing module named PyQt4 - imported by pandas.io.clipboard (delayed, conditional, optional)
missing module named qtpy - imported by pandas.io.clipboard (delayed, conditional, optional)
missing module named pysqlcipher3 - imported by sqlalchemy.dialects.sqlite.pysqlcipher (delayed)
missing module named sqlcipher3 - imported by sqlalchemy.dialects.sqlite.pysqlcipher (delayed, optional)
missing module named psycopg2 - imported by sqlalchemy.dialects.postgresql.psycopg2 (delayed)
missing module named 'psycopg.pq' - imported by sqlalchemy.dialects.postgresql.psycopg (delayed)
missing module named 'psycopg.types' - imported by sqlalchemy.dialects.postgresql.psycopg (delayed, conditional)
missing module named 'psycopg.adapt' - imported by sqlalchemy.dialects.postgresql.psycopg (delayed, conditional)
missing module named psycopg - imported by sqlalchemy.dialects.postgresql.psycopg (delayed, conditional)
missing module named asyncpg - imported by sqlalchemy.dialects.postgresql.asyncpg (delayed)
missing module named oracledb - imported by sqlalchemy.dialects.oracle.oracledb (delayed, conditional)
missing module named cx_Oracle - imported by sqlalchemy.dialects.oracle.cx_oracle (delayed)
missing module named 'mysql.connector' - imported by sqlalchemy.dialects.mysql.mysqlconnector (delayed, conditional, optional)
missing module named mysql - imported by sqlalchemy.dialects.mysql.mysqlconnector (delayed)
missing module named asyncmy - imported by sqlalchemy.dialects.mysql.asyncmy (delayed)
missing module named nacl - imported by pymysql._auth (delayed, optional)
missing module named 'cryptography.hazmat' - imported by pymysql._auth (optional)
missing module named tables - imported by pandas.io.pytables (delayed, conditional)
missing module named 'pyarrow.fs' - imported by pandas.io.orc (conditional)
missing module named fsspec - imported by pandas.io.orc (conditional)
missing module named 'pyarrow.parquet' - imported by pandas.io.parquet (delayed)
missing module named google - imported by pandas.io.gbq (conditional)
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.command' - imported by numpy.f2py.diagnose (delayed, conditional, optional)
missing module named numpy_distutils - imported by numpy.f2py.diagnose (delayed, optional)
missing module named yaml - imported by numpy.__config__ (delayed)
missing module named numpy._distributor_init_local - imported by numpy (optional), numpy._distributor_init (optional)
missing module named pytest - imported by pandas._testing._io (delayed), pandas._testing (delayed)
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)

File diff suppressed because it is too large Load Diff

185
invalid_scheduler.py Normal file
View File

@@ -0,0 +1,185 @@
import requests
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from main import DeviceAutomation
import globals.apis as apis
import random
from datetime import datetime, timedelta
## --- 配置区 ---
API_URL = "http://your-api-server.com/api/tasks" # 替换为真实的接口地址
MAX_WORKERS = 3 # 最大并发线程数,建议根据网络带宽调整
TIME_FILE_PATH = r"D:\time.txt" # 电脑D盘下的路径
def parse_time_config():
"""
解析 D:\time.txt 文件
返回格式: { "用户名": "16:40:20", ... }
"""
time_map = {}
if not os.path.exists(TIME_FILE_PATH):
print(f"⚠️ 未找到配置文件: {TIME_FILE_PATH}")
return time_map
try:
with open(TIME_FILE_PATH, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
# 使用正则匹配用户名、时间、状态
# 兼容 wangshun 16:40:20 true 和 cdwzq3liangchaoyong 15:06:35 true
match = re.search(r'(\w+)\s+(\d{2}:\d{2}:\d{2})\s+true', line)
if match:
username = match.group(1)
scheduled_time = match.group(2)
time_map[username] = scheduled_time
except Exception as e:
print(f"❌ 解析 time.txt 失败: {e}")
return time_map
def get_remote_tasks():
"""
从接口获取数据
"""
try:
# 1. 先获取本地文件中的配置
local_times = parse_time_config()
if not local_times:
print("❌ time.txt 中没有有效的 true 任务或文件为空")
return {}
# 2. 从服务器获取账户
accounts = apis.get_accounts_from_server("68c0dbfdb7cbcd616e7c5ab5")
if not accounts:
print("❌ 未从服务器获取到账户信息,终止流程")
return {}
# filtered_accounts = [account for account in accounts if account.get('is_ok') == 1]
# # print("✅ 获取账户信息成功", filtered_accounts)
# # current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# # 从原始 accounts 数据中筛选有 device_ip 和 device_port 且不为 None 的账户
# device_dict = {}
# for account in filtered_accounts:
# device_ip = account.get('device_ip')
# device_port = account.get('device_port')
# # 检查 device_ip 和 device_port 是否都存在且不为 None
# if device_ip and device_port:
# # 拼接为 ip:port 格式
# key = f"{device_ip}:{device_port}"
# # 添加当前时间
# # device_dict[key] = current_time
# # 获取当前时间的 datetime 对象
# current_datetime = datetime.now()
# # 生成1-2小时的随机时间
# random_hours = random.uniform(1, 2)
# # 计算未来时间datetime 对象可以加 timedelta
# future_datetime = current_datetime + timedelta(hours=random_hours)
# # 将 datetime 对象格式化为字符串
# time_str = future_datetime.strftime("%Y-%m-%d %H:%M:%S")
# # 添加时间字符串到字典
# device_dict[key] = time_str
# # 结果
# print(device_dict)
# return device_dict
device_dict = {}
today_str = datetime.now().strftime("%Y-%m-%d")
for account in accounts:
# 条件1: 接口返回 is_ok == 1
if account.get('is_ok') != 1:
continue
username = account.get('username') # 假设接口中用户名的 key 是 username
device_ip = account.get('device_ip')
device_port = account.get('device_port')
# 条件2: 用户名在 time.txt 中且状态为 true (已经在 local_times 过滤过)
if username in local_times and device_ip and device_port:
key = f"{device_ip}:{device_port}"
# 拼接成完整的 YYYY-MM-DD HH:MM:SS
full_time_str = f"{today_str} {local_times[username]}"
device_dict[key] = full_time_str
print(f"✅ 匹配成功,待执行任务详情: {device_dict}")
return device_dict
# # 模拟数据供测试
# return {
# "192.168.1.45:5555": "2026-02-03 10:20:00",
# "192.168.1.100:5556": "2026-02-03 10:20:20",
# "192.168.31.12:5557": "2026-02-03 10:21:00"
# }
except Exception as e:
print(f"❌ 获取接口任务失败: {e}")
return {}
def run_task(address, target_time):
"""
单个线程的任务包装器
"""
# 格式化账号/设备名:确保带上端口号
device_address = address
print(f"🕒 [等待/启动] 设备: {device_address} | 预定时间: {target_time}")
# 解析目标时间
target_datetime = datetime.strptime(target_time, "%Y-%m-%d %H:%M:%S")
current_datetime = datetime.now()
# 计算等待时间
wait_seconds = (target_datetime - current_datetime).total_seconds()
# 如果需要等待
if wait_seconds > 0:
print(f"⏳ [等待中] 设备: {device_address} | 等待时间: {wait_seconds:.2f}")
time.sleep(wait_seconds)
print(f"▶️ [开始执行] 设备: {device_address} | 当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
else:
print(f"▶️ [开始执行] 设备: {device_address} | 当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
try:
# 创建DeviceAutomation实例并执行上传逻辑
automation = DeviceAutomation(device_address)
result = automation.handle_app_state()
return f"{device_address} 完成: {result}"
except Exception as e:
return f"{device_address} 报错: {str(e)}"
def monitor_center():
"""调度中心"""
tasks_data = get_remote_tasks()
if not tasks_data:
print("📭 接口未返回任何任务,程序退出。")
return
print(f"🗂️ 发现 {len(tasks_data)} 个待处理账号,开始建立线程池...")
# 使用线程池并发执行
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
# 提交所有任务
future_to_device = {
executor.submit(run_task, acc, t): acc
for acc, t in tasks_data.items()
}
# 实时打印完成情况
for future in as_completed(future_to_device):
print(future.result())
if __name__ == "__main__":
print("🚀 自动化调度程序启动...")
monitor_center()
print("🏁 所有并发任务处理序列结束。")

View File

@@ -131,10 +131,7 @@ class DeviceAutomation(object):
logging.info(f"设备 {self.device_id} 登录成功,继续执行更新操作")
time.sleep(1)
# 执行更新操作
if not self.download_tabbar_page.download_tabbar_page_manager():
logging.error(f"设备 {self.device_id} 更新操作执行失败")
return False
# 获取状态为3的线路。
apis.get_line_info_and_save_global(user_name=global_variable.get_username());

View File

@@ -1,478 +0,0 @@
# actions.py 主自动化脚本
import os
import logging
import time
import subprocess
from appium import webdriver
from appium.options.android import UiAutomator2Options
from appium.webdriver.common.appiumby import AppiumBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException, NoSuchElementException
import globals.ids as ids
import globals.global_variable as global_variable # 导入全局变量模块
import permissions # 导入权限处理模块
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 page_objects.login_page import LoginPage
from page_objects.download_tabbar_page import DownloadTabbarPage
from page_objects.screenshot_page import ScreenshotPage
from page_objects.upload_config_page import UploadConfigPage
from page_objects.more_download_page import MoreDownloadPage
# 配置日志
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s: %(message)s",
handlers=[
logging.FileHandler("appium_automation.log"),
logging.StreamHandler()
]
)
class DeviceAutomation:
@staticmethod
def get_device_id() -> str:
# """
# 获取设备ID优先使用已连接设备否则使用全局配置
# """
# try:
# # 检查已连接设备
# result = subprocess.run(
# ["adb", "devices"],
# capture_output=True,
# text=True,
# timeout=10
# )
# # 解析设备列表
# for line in result.stdout.strip().split('\n')[1:]:
# if line.strip() and "device" in line and "offline" not in line:
# device_id = line.split('\t')[0]
# logging.info(f"使用已连接设备: {device_id}")
# global_variable.GLOBAL_DEVICE_ID = device_id
# return device_id
# except Exception as e:
# logging.warning(f"设备检测失败: {e}")
"""
获取设备ID优先使用无线连接设备否则尝试开启无线调试最后使用全局配置
"""
try:
# 检查已连接设备
result = subprocess.run(
["adb", "devices"],
capture_output=True,
text=True,
timeout=10
)
# 解析设备列表
wireless_device_id = None
usb_device_id = None
# 先查找无线连接的设备IP:端口格式)
for line in result.stdout.strip().split('\n')[1:]:
if line.strip() and "device" in line and "offline" not in line:
current_device = line.split('\t')[0]
# 检查是否为IP:端口格式的无线连接
if ":" in current_device and any(char.isdigit() for char in current_device):
wireless_device_id = current_device
logging.info(f"使用无线连接设备: {wireless_device_id}")
global_variable.GLOBAL_DEVICE_ID = wireless_device_id
return wireless_device_id
else:
# 记录第一个USB连接的设备
if not usb_device_id:
usb_device_id = current_device
# 如果没有找到无线连接的设备尝试使用USB设备开启无线调试
if not wireless_device_id and usb_device_id:
logging.info(f"未找到无线连接设备尝试使用USB设备 {usb_device_id} 开启无线调试")
# 尝试获取设备IP地址
try:
import re
import time
ip_result = subprocess.run(
["adb", "-s", usb_device_id, "shell", "ip", "-f", "inet", "addr", "show", "wlan0"],
capture_output=True,
text=True,
timeout=10
)
# 解析IP地址
ip_output = ip_result.stdout
if "inet " in ip_output:
# 提取IP地址
ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_output)
if ip_match:
device_ip = ip_match.group(1)
logging.info(f"获取到设备IP地址: {device_ip}")
# 开启无线调试
tcpip_result = subprocess.run(
["adb", "-s", usb_device_id, "tcpip", "5555"],
capture_output=True,
text=True,
timeout=10
)
if "restarting in TCP mode port: 5555" in tcpip_result.stdout:
logging.info("无线调试已开启,端口: 5555")
# 等待几秒钟让设备准备好
time.sleep(3)
# 连接到无线设备
connect_result = subprocess.run(
["adb", "connect", f"{device_ip}:5555"],
capture_output=True,
text=True,
timeout=10
)
if "connected to" in connect_result.stdout:
logging.info(f"成功连接到无线设备: {device_ip}:5555")
global_variable.GLOBAL_DEVICE_ID = f"{device_ip}:5555"
return f"{device_ip}:5555"
else:
logging.warning(f"连接无线设备失败: {connect_result.stderr}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning(f"开启无线调试失败: {tcpip_result.stderr}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning("未找到设备IP地址")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
else:
logging.warning("无法获取设备IP地址可能设备未连接到WiFi")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
except Exception as e:
logging.warning(f"开启无线调试时出错: {str(e)}")
logging.info(f"使用USB设备: {usb_device_id}")
global_variable.GLOBAL_DEVICE_ID = usb_device_id
return usb_device_id
except Exception as e:
logging.warning(f"设备检测失败: {e}")
# 使用全局配置
device_id = global_variable.GLOBAL_DEVICE_ID
logging.info(f"使用全局配置设备: {device_id}")
return device_id
def __init__(self, device_id=None):
# 如果没有提供设备ID则自动获取
if device_id is None:
self.device_id = self.get_device_id()
else:
self.device_id = device_id
# 初始化权限
if permissions.grant_appium_permissions(self.device_id):
logging.info(f"设备 {self.device_id} 权限授予成功")
else:
logging.warning(f"设备 {self.device_id} 权限授予失败")
# 确保Appium服务器正在运行
ensure_appium_server_running(4723)
# 初始化驱动
self.init_driver()
# 先拼接,后创建测试结果目录
self.results_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'test_results')
os.makedirs(self.results_dir, exist_ok=True)
def init_driver(self):
"""初始化Appium驱动"""
try:
# 使用全局函数初始化驱动
self.driver, self.wait = init_appium_driver(self.device_id)
# 初始化页面对象
logging.info(f"设备 {self.device_id} 开始初始化页面对象")
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.upload_config_page = UploadConfigPage(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} 所有页面对象初始化完成")
# 检查应用是否成功启动
if is_app_launched(self.driver):
logging.info(f"设备 {self.device_id} 沉降观测App已成功启动")
else:
logging.warning(f"设备 {self.device_id} 应用可能未正确启动")
# 手动启动应用
launch_app_manually(self.driver, self.device_id)
except Exception as e:
logging.error(f"设备 {self.device_id} 初始化驱动失败: {str(e)}")
raise
def is_app_launched(self):
"""检查应用是否已启动"""
try:
return is_app_launched(self.driver)
except Exception as e:
logging.error(f"设备 {self.device_id} 检查应用启动状态时出错: {str(e)}")
return False
def is_element_present(self, by, value):
"""检查元素是否存在"""
try:
self.driver.find_element(by, value)
return True
except NoSuchElementException:
return False
def handle_app_state(self):
"""根据当前应用状态处理相应的操作"""
try:
login_btn_exists = self.login_page.is_login_page()
if not login_btn_exists:
logging.error(f"设备 {self.device_id} 未知应用状态,无法确定当前页面,跳转到登录页面")
if self.navigate_to_login_page(self.driver, self.device_id):
logging.info(f"设备 {self.device_id} 成功跳转到登录页面")
return self.handle_app_state() # 递归调用处理登录后的状态
else:
logging.error(f"设备 {self.device_id} 跳转到登录页面失败")
return False
# 处理登录页面状态
logging.info(f"设备 {self.device_id} 检测到登录页面,执行登录操作")
max_retries = 1
login_success = False
for attempt in range(max_retries + 1):
if self.login_page.login():
login_success = True
break
else:
if attempt < max_retries:
logging.warning(f"设备 {self.device_id} 登录失败,准备重试 ({attempt + 1}/{max_retries})")
time.sleep(2) # 等待2秒后重试
else:
logging.error(f"设备 {self.device_id} 登录失败,已达到最大重试次数")
if not login_success:
return False
logging.info(f"设备 {self.device_id} 登录成功,继续执行更新操作")
time.sleep(1)
# 执行更新操作
if not self.download_tabbar_page.download_tabbar_page_manager():
logging.error(f"设备 {self.device_id} 更新操作执行失败")
return False
# 获取状态为3的线路。
apis.get_line_info_and_save_global(user_name=global_variable.GLOBAL_USERNAME);
# # 虚拟数据替代
# global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT = {'CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原': 'L156372', 'CDWZQ-2标-蓝家湾特大 桥-31-31-平原': 'L159206'}
# global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST = list(global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.keys())
# 点击测量导航栏按钮
measure_page_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_3_layout"))
)
measure_page_btn.click()
# 处理平差
self.screenshot_page.screenshot_page_manager(self.device_id)
# 检查是否有需要上传的断点
if not global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST:
logging.info(f"设备 {self.device_id} 断点列表为空,无需执行上传操作")
return False
# 点击上传导航栏按钮
upload_page_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/img_2_layout"))
)
upload_page_btn.click()
# 遍历断点列表,逐个执行上传操作
upload_success_count = 0
for breakpoint_name in global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST:
try:
logging.info(f"设备 {self.device_id} 开始处理断点 '{breakpoint_name}' 的上传")
# 安全地获取断点信息
line_num = global_variable.GLOBAL_UPLOAD_BREAKPOINT_DICT.get(breakpoint_name)
if line_num is None:
logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 在字典中未找到,跳过上传")
continue
if not line_num:
logging.warning(f"设备 {self.device_id} 断点 '{breakpoint_name}' 未获取到line_num跳过上传")
continue
# 执行上传配置管理,传入当前断点名称
if self.upload_config_page.upload_config_page_manager(self.results_dir, breakpoint_name, line_num):
logging.info(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传成功")
upload_success_count += 1
else:
logging.error(f"设备 {self.device_id} 断点 '{breakpoint_name}' 上传失败")
except Exception as e:
logging.error(f"设备 {self.device_id} 处理断点 '{breakpoint_name}' 时发生异常: {str(e)}")
logging.info(f"设备 {self.device_id} 上传配置管理执行完成,成功上传 {upload_success_count}/{len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)} 个断点")
# 如果所有断点都上传成功返回True否则返回False
all_upload_success = upload_success_count == len(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)
if all_upload_success:
logging.info(f"设备 {self.device_id} 所有断点上传成功")
# 把上传成功的断点写入日志文件"上传成功的断点.txt"
with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f:
for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST:
f.write(f"{bp}\n")
else:
logging.warning(f"设备 {self.device_id} 部分断点上传失败")
# 把上传成功的断点写入日志文件"上传成功的断点.txt"
with open(os.path.join(self.results_dir, "上传成功的断点.txt"), "w", encoding='utf-8') as f:
for bp in global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST:
f.write(f"{bp}\n")
# 把上传失败的断点写入日志文件"上传失败的断点.txt"
with open(os.path.join(self.results_dir, "上传失败的断点.txt"), "w", encoding='utf-8') as f:
for bp in set(global_variable.GLOBAL_UPLOAD_BREAKPOINT_LIST)-set(global_variable.GLOBAL_UPLOAD_SUCCESS_BREAKPOINT_LIST):
f.write(f"{bp}\n")
return all_upload_success
except Exception as e:
logging.error(f"设备 {self.device_id} 处理应用状态时出错: {str(e)}")
return False
def check_and_click_confirm_popup_appium(self):
"""
适用于Appium的弹窗检测函数
Returns:
bool: 是否成功处理弹窗
"""
try:
from appium.webdriver.common.appiumby import AppiumBy
# 使用self.driver而不是参数
if not hasattr(self, 'driver') or self.driver is None:
logging.warning("driver未初始化无法检测弹窗")
return False
# 检查弹窗消息
message_elements = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.TextView[@text="是否退出测量界面?"]')
if message_elements:
logging.info("检测到退出测量界面弹窗")
# 点击"是"按钮
confirm_buttons = self.driver.find_elements(AppiumBy.XPATH, '//android.widget.Button[@text="" and @resource-id="android:id/button1"]')
if confirm_buttons:
confirm_buttons[0].click()
logging.info("已点击''按钮")
time.sleep(1)
return True
else:
logging.warning("未找到''按钮")
return False
else:
return False
except Exception as e:
logging.error(f"Appium检测弹窗时发生错误: {str(e)}")
return False
def navigate_to_login_page(self, driver, device_id):
"""
补充的跳转页面函数:当设备处于未知状态时,尝试跳转到登录页面
参数:
driver: 已初始化的Appium WebDriver对象
device_id: 设备ID用于日志记录
"""
try:
target_package = 'com.bjjw.cjgc'
target_activity = '.activity.LoginActivity'
# 使用ADB命令启动Activity
try:
logging.info(f"尝试使用ADB命令启动LoginActivity: {target_package}/{target_activity}")
adb_command = f"adb -s {device_id} shell am start -n {target_package}/{target_activity}"
result = subprocess.run(adb_command, shell=True, capture_output=True, text=True)
if result.returncode == 0:
logging.info(f"使用ADB命令启动LoginActivity成功")
time.sleep(2) # 等待Activity启动
return True
else:
logging.warning(f"ADB命令执行失败: {result.stderr}")
except Exception as adb_error:
logging.warning(f"执行ADB命令时出错: {adb_error}")
except Exception as e:
logging.error(f"跳转到登录页面过程中发生未预期错误: {e}")
# 所有尝试都失败
return False
def run_automation(self):
"""运行自动化流程"""
try:
success = self.handle_app_state()
# success = self.test_handle_app_state()
if success:
logging.info(f"设备 {self.device_id} 自动化流程执行成功")
else:
logging.error(f"设备 {self.device_id} 自动化流程执行失败")
return success
except Exception as e:
logging.error(f"设备 {self.device_id} 自动化执行过程中发生错误: {str(e)}")
return False
finally:
self.quit()
def quit(self):
"""关闭驱动"""
safe_quit_driver(getattr(self, 'driver', None), self.device_id)
# 主执行逻辑
if __name__ == "__main__":
# 单个设备配置 - 现在DeviceAutomation会自动获取设备ID
try:
automation = DeviceAutomation()
success = automation.run_automation()
if success:
logging.info(f"设备自动化流程执行成功")
else:
logging.error(f"设备自动化流程执行失败")
except Exception as e:
logging.error(f"设备执行出错: {str(e)}")

Binary file not shown.

View File

@@ -1,46 +0,0 @@
import socket
def send_tcp_command(command: str, host: str = '127.0.0.1', port: int = 8888, encoding: str = 'utf-8') -> bool:
"""
向指定TCP端口发送指令
参数:
command: 要发送的指令字符串
host: 目标主机地址默认127.0.0.1
port: 目标端口默认8888
encoding: 字符串编码格式默认utf-8
返回:
发送成功返回True失败返回False
"""
# 创建TCP socket并自动关闭with语句确保资源释放
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
try:
# 连接服务器超时时间5秒避免无限阻塞
sock.settimeout(5.0)
sock.connect((host, port))
# 发送指令(转换为字节流)
sock.sendall(command.encode(encoding))
print(f"指令 '{command}' 发送成功")
return True
except ConnectionRefusedError:
print(f"连接失败:{host}:{port} 未监听或不可达")
except socket.timeout:
print(f"连接超时超过5秒未连接到 {host}:{port}")
except UnicodeEncodeError:
print(f"编码失败:指令包含{encoding}无法编码的字符")
except Exception as e:
print(f"发送失败:{str(e)}")
return False
# 使用示例
if __name__ == "__main__":
# 发送StartConnect指令
send_tcp_command("StartConnect")
# 也可以发送其他指令,例如:
# send_tcp_command("StopConnect")

View File

@@ -633,10 +633,13 @@ class UploadConfigPage:
def _load_user_data(self):
"""加载用户数据从Excel文件只提取名字和身份证到字典"""
try:
# 默认路径:当前脚本的上一级目录下的"上传人员信息.xlsx"
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
excel_path = os.path.join(parent_dir, "上传人员信息.xlsx")
# # 默认路径:当前脚本的上一级目录下的"上传人员信息.xlsx"
# current_dir = os.path.dirname(os.path.abspath(__file__))
# parent_dir = os.path.dirname(current_dir)
# excel_path = os.path.join(parent_dir, "上传人员信息.xlsx")
# 固定路径D:\uploadInfo\上传人员信息.xlsx
excel_path = os.path.join("D:\\", "uploadInfo", "上传人员信息.xlsx")
if not os.path.exists(excel_path):
logging.error(f"Excel文件不存在: {excel_path}")
@@ -1535,16 +1538,24 @@ class UploadConfigPage:
save_upload_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((AppiumBy.ID, "com.bjjw.cjgc:id/improve_save_btn"))
)
max_retries = 3
for attempt in range(max_retries):
save_upload_btn.click()
self.logger.info("已点击保存上传按钮")
self.logger.info(f"已点击保存上传按钮 (第 {attempt + 1} 次)")
# 处理警告弹窗
time.sleep(1)
if not self.handle_warning_dialog():
self.logger.error("处理警告弹窗失败")
return False
if self.handle_warning_dialog():
self.logger.info("处理警告弹窗成功")
return True
else:
self.logger.warning(f"处理警告弹窗失败,准备重试 ({attempt + 1}/{max_retries})")
if attempt < max_retries - 1:
time.sleep(1)
self.logger.error(f"处理警告弹窗失败,已达到最大重试次数 {max_retries}")
return False
except TimeoutException:
self.logger.error("点击保存上传按钮超时")
@@ -1821,6 +1832,12 @@ class UploadConfigPage:
self.logger.error("获取工况信息失败")
return False
# 遍历返回数据的所有workinfoname如果为空就打印当前在上传的线路名称
for workinfo in work_conditions.values():
if not workinfo.get('workinfoname'):
print(f"❌❌❌❌❌线路编码 '{self.line_num}' 中有工况缺失(请复制发送编码给相关人员)❌❌❌❌❌")
return False
# 提取人员姓名和身份证
if not self._load_user_data():
self.logger.error("加载用户数据失败")

View File

@@ -1,185 +1,193 @@
import requests
import time
import os
import re
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from main import DeviceAutomation
import globals.apis as apis
import random
from datetime import datetime, timedelta
## --- 配置区 ---
API_URL = "http://your-api-server.com/api/tasks" # 替换为真实的接口地址
MAX_WORKERS = 3 # 最大并发线程数,建议根据网络带宽调整
TIME_FILE_PATH = r"D:\time.txt" # 电脑D盘下的路径
API_URL = "http://your-api-server.com/api/tasks"
MAX_WORKERS = 3
TIME_FILE_PATH = r"D:\uploadInfo\time.txt"
POLLING_INTERVAL = 600 # 10分钟轮询一次
# 文件操作锁,防止多线程同时写入文件造成冲突
file_lock = threading.Lock()
def update_file_status(username, from_status, to_status):
"""
安全地更新 time.txt 中该用户的状态
例如: 将 'true' 改为 'running', 或将 'running' 改为 'done'
"""
if not os.path.exists(TIME_FILE_PATH):
return False
success = False
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:
print(f"❌ 更新文件状态失败 ({username}): {e}")
return False
def parse_time_config():
"""
解析 D:\time.txt 文件
返回格式: { "用户名": "16:40:20", ... }
解析 time.txt只获取状态为 true 的任务
"""
time_map = {}
if not os.path.exists(TIME_FILE_PATH):
print(f"⚠️ 未找到配置文件: {TIME_FILE_PATH}")
print(f"⚠️ 文件不存在: {TIME_FILE_PATH}")
return time_map
try:
with file_lock:
with open(TIME_FILE_PATH, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
if not line:
continue
# 使用正则匹配用户名、时间、状态
# 兼容 wangshun 16:40:20 true 和 cdwzq3liangchaoyong 15:06:35 true
match = re.search(r'(\w+)\s+(\d{2}:\d{2}:\d{2})\s+true', line)
# 匹配:用户名 时间 true (仅获取待处理任务)
match = re.search(r'(\w+)\s+(\d{1,2}:\d{2}:\d{2})\s+true$', line)
if match:
username = match.group(1)
scheduled_time = match.group(2)
username, scheduled_time = match.group(1), match.group(2)
time_map[username] = scheduled_time
except Exception as e:
print(f"❌ 解析 time.txt 失败: {e}")
return time_map
def get_remote_tasks():
def get_combined_tasks():
"""
从接口获取数据
结合接口(is_ok==1)和本地文件(true)筛选任务
"""
try:
# 1. 先获取本地文件中的配置
local_times = parse_time_config()
if not local_times:
print("❌ time.txt 中没有有效的 true 任务或文件为空")
return {}
# 2. 从服务器获取账户
# 调用你的 API 接口获取账号信息
accounts = apis.get_accounts_from_server("68c0dbfdb7cbcd616e7c5ab5")
if not accounts:
print("❌ 未从服务器获取到账户信息,终止流程")
return {}
# filtered_accounts = [account for account in accounts if account.get('is_ok') == 1]
# # print("✅ 获取账户信息成功", filtered_accounts)
# # current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# # 从原始 accounts 数据中筛选有 device_ip 和 device_port 且不为 None 的账户
# device_dict = {}
# for account in filtered_accounts:
# device_ip = account.get('device_ip')
# device_port = account.get('device_port')
# # 检查 device_ip 和 device_port 是否都存在且不为 None
# if device_ip and device_port:
# # 拼接为 ip:port 格式
# key = f"{device_ip}:{device_port}"
# # 添加当前时间
# # device_dict[key] = current_time
# # 获取当前时间的 datetime 对象
# current_datetime = datetime.now()
# # 生成1-2小时的随机时间
# random_hours = random.uniform(1, 2)
# # 计算未来时间datetime 对象可以加 timedelta
# future_datetime = current_datetime + timedelta(hours=random_hours)
# # 将 datetime 对象格式化为字符串
# time_str = future_datetime.strftime("%Y-%m-%d %H:%M:%S")
# # 添加时间字符串到字典
# device_dict[key] = time_str
# # 结果
# print(device_dict)
# return device_dict
device_dict = {}
today_str = datetime.now().strftime("%Y-%m-%d")
task_list = {}
today = datetime.now().strftime("%Y-%m-%d")
for account in accounts:
# 条件1: 接口返回 is_ok == 1
if account.get('is_ok') != 1:
continue
if account.get('is_ok') == 1:
user = account.get('username')
ip = account.get('device_ip')
port = account.get('device_port')
username = account.get('username') # 假设接口中用户名的 key 是 username
device_ip = account.get('device_ip')
device_port = account.get('device_port')
# 只有在 time.txt 中是 true 的账号才会被加入
if user in local_times and ip and port:
address = f"{ip}:{port}"
# full_time = f"{today} {local_times[user]}"
# 确保时间是两位数格式
raw_time = local_times[user]
# 将时间格式化为两位数9:52:20 -> 09:52:20
if ':' in raw_time:
parts = raw_time.split(':')
if len(parts[0]) == 1:
raw_time = f"0{raw_time}" # 补齐前导零
# 条件2: 用户名在 time.txt 中且状态为 true (已经在 local_times 过滤过)
if username in local_times and device_ip and device_port:
key = f"{device_ip}:{device_port}"
# 拼接成完整的 YYYY-MM-DD HH:MM:SS
full_time_str = f"{today_str} {local_times[username]}"
device_dict[key] = full_time_str
full_time = f"{today} {raw_time}"
task_list[address] = {"time": full_time, "user": user}
print(f"✅ 匹配成功,待执行任务详情: {device_dict}")
return device_dict
# # 模拟数据供测试
# return {
# "192.168.1.45:5555": "2026-02-03 10:20:00",
# "192.168.1.100:5556": "2026-02-03 10:20:20",
# "192.168.31.12:5557": "2026-02-03 10:21:00"
# }
return task_list
except Exception as e:
print(f"❌ 获取接口任务失败: {e}")
print(f"❌ 获取任务异常: {e}")
return {}
def run_task(address, target_time):
def run_task(address, target_time, username):
"""
单个线程的任务包装器
单个执行线程:锁定状态 -> 等待 -> 执行 -> 完成
"""
# 格式化账号/设备名:确保带上端口号
device_address = address
print(f"🕒 [等待/启动] 设备: {device_address} | 预定时间: {target_time}")
# 解析目标时间
target_datetime = datetime.strptime(target_time, "%Y-%m-%d %H:%M:%S")
current_datetime = datetime.now()
# 计算等待时间
wait_seconds = (target_datetime - current_datetime).total_seconds()
# 如果需要等待
if wait_seconds > 0:
print(f"⏳ [等待中] 设备: {device_address} | 等待时间: {wait_seconds:.2f}")
time.sleep(wait_seconds)
print(f"▶️ [开始执行] 设备: {device_address} | 当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
else:
print(f"▶️ [开始执行] 设备: {device_address} | 当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
# 1. 尝试将状态从 true 改为 running (锁定任务)
# 如果此时文件状态已被其他逻辑修改,则放弃执行,防止重复
if not update_file_status(username, "true", "running"):
return f"⏭️ {username} 状态已变更,跳过执行。"
print(f"🚀 [任务锁定] 设备: {address} | 用户: {username} | 计划时间: {target_time}")
try:
# 创建DeviceAutomation实例并执行上传逻辑
automation = DeviceAutomation(device_address)
# 2. 计算并执行等待逻辑
target_dt = datetime.strptime(target_time, "%Y-%m-%d %H:%M:%S")
wait_secs = (target_dt - datetime.now()).total_seconds()
if wait_secs > 0:
print(f"{username} 距离执行还有 {int(wait_secs)} 秒...")
time.sleep(wait_secs)
# 3. 调用 main.py 中的自动化逻辑
print(f"▶️ [正在执行] {username} 开始自动化操作...")
automation = DeviceAutomation(address)
result = automation.handle_app_state()
return f"{device_address} 完成: {result}"
# 4. 执行完成后,将状态从 running 改为 done
update_file_status(username, "running", "done")
return f"{username} 执行成功: {result}"
except Exception as e:
return f"{device_address} 报错: {str(e)}"
# 如果中间报错,将状态改为 error 方便排查
update_file_status(username, "running", "error")
return f"{username} 执行异常: {str(e)}"
def monitor_center():
"""调度中心"""
tasks_data = get_remote_tasks()
"""调度中心:每半小时检查一次"""
while True:
now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"\n{'='*20} 周期性检查开始 ({now_str}) {'='*20}")
if not tasks_data:
print("📭 接口未返回任何任务,程序退出。")
return
tasks = get_combined_tasks()
print(f"🗂️ 发现 {len(tasks_data)} 个待处理账号,开始建立线程池...")
# 使用线程池并发执行
if tasks:
print(f"📡 发现 {len(tasks)} 个符合条件且未跑过的任务,准备启动线程池...")
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
# 提交所有任务
future_to_device = {
executor.submit(run_task, acc, t): acc
for acc, t in tasks_data.items()
# 提交任务,将 address, time, username 传入
future_to_user = {
executor.submit(run_task, addr, info['time'], info['user']): info['user']
for addr, info in tasks.items()
}
# 实时打印完成情况
for future in as_completed(future_to_device):
print(future.result())
for f in as_completed(future_to_user):
print(f.result())
else:
print("📭 暂无待处理任务 (所有账号已完成或 is_ok != 1)")
print(f"💤 本轮轮询结束,等待 {POLLING_INTERVAL//60} 分钟进行下次检查...")
time.sleep(POLLING_INTERVAL)
if __name__ == "__main__":
print("🚀 自动化调度程序启动...")
print("🌟 自动化调度服务已启动")
print(f"📂 配置文件: {TIME_FILE_PATH}")
print(f"⏱️ 轮询频率: {POLLING_INTERVAL}")
try:
monitor_center()
print("🏁 所有并发任务处理序列结束。")
except KeyboardInterrupt:
print("\n👋 用户手动停止程序。")

View File

@@ -1,193 +0,0 @@
import time
import os
import re
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed
from datetime import datetime
from main import DeviceAutomation
import globals.apis as apis
## --- 配置区 ---
API_URL = "http://your-api-server.com/api/tasks"
MAX_WORKERS = 3
TIME_FILE_PATH = r"D:\time.txt"
POLLING_INTERVAL = 300 # 30分钟轮询一次
# 文件操作锁,防止多线程同时写入文件造成冲突
file_lock = threading.Lock()
def update_file_status(username, from_status, to_status):
"""
安全地更新 time.txt 中该用户的状态
例如: 将 'true' 改为 'running', 或将 'running' 改为 'done'
"""
if not os.path.exists(TIME_FILE_PATH):
return False
success = False
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:
print(f"❌ 更新文件状态失败 ({username}): {e}")
return False
def parse_time_config():
"""
解析 time.txt只获取状态为 true 的任务
"""
time_map = {}
if not os.path.exists(TIME_FILE_PATH):
print(f"⚠️ 文件不存在: {TIME_FILE_PATH}")
return time_map
try:
with file_lock:
with open(TIME_FILE_PATH, 'r', encoding='utf-8') as f:
for line in f:
line = line.strip()
# 匹配:用户名 时间 true (仅获取待处理任务)
match = re.search(r'(\w+)\s+(\d{1,2}:\d{2}:\d{2})\s+true$', line)
if match:
username, scheduled_time = match.group(1), match.group(2)
time_map[username] = scheduled_time
except Exception as e:
print(f"❌ 解析 time.txt 失败: {e}")
return time_map
def get_combined_tasks():
"""
结合接口(is_ok==1)和本地文件(true)筛选任务
"""
try:
local_times = parse_time_config()
if not local_times:
return {}
# 调用你的 API 接口获取账号信息
accounts = apis.get_accounts_from_server("68c0dbfdb7cbcd616e7c5ab5")
if not accounts:
return {}
task_list = {}
today = datetime.now().strftime("%Y-%m-%d")
for account in accounts:
if account.get('is_ok') == 1:
user = account.get('username')
ip = account.get('device_ip')
port = account.get('device_port')
# 只有在 time.txt 中是 true 的账号才会被加入
if user in local_times and ip and port:
address = f"{ip}:{port}"
# full_time = f"{today} {local_times[user]}"
# 确保时间是两位数格式
raw_time = local_times[user]
# 将时间格式化为两位数9:52:20 -> 09:52:20
if ':' in raw_time:
parts = raw_time.split(':')
if len(parts[0]) == 1:
raw_time = f"0{raw_time}" # 补齐前导零
full_time = f"{today} {raw_time}"
task_list[address] = {"time": full_time, "user": user}
return task_list
except Exception as e:
print(f"❌ 获取任务异常: {e}")
return {}
def run_task(address, target_time, username):
"""
单个执行线程:锁定状态 -> 等待 -> 执行 -> 完成
"""
# 1. 尝试将状态从 true 改为 running (锁定任务)
# 如果此时文件状态已被其他逻辑修改,则放弃执行,防止重复
if not update_file_status(username, "true", "running"):
return f"⏭️ {username} 状态已变更,跳过执行。"
print(f"🚀 [任务锁定] 设备: {address} | 用户: {username} | 计划时间: {target_time}")
try:
# 2. 计算并执行等待逻辑
target_dt = datetime.strptime(target_time, "%Y-%m-%d %H:%M:%S")
wait_secs = (target_dt - datetime.now()).total_seconds()
if wait_secs > 0:
print(f"{username} 距离执行还有 {int(wait_secs)} 秒...")
time.sleep(wait_secs)
# 3. 调用 main.py 中的自动化逻辑
print(f"▶️ [正在执行] {username} 开始自动化操作...")
automation = DeviceAutomation(address)
result = automation.handle_app_state()
# 4. 执行完成后,将状态从 running 改为 done
update_file_status(username, "running", "done")
return f"{username} 执行成功: {result}"
except Exception as e:
# 如果中间报错,将状态改为 error 方便排查
update_file_status(username, "running", "error")
return f"{username} 执行异常: {str(e)}"
def monitor_center():
"""调度中心:每半小时检查一次"""
while True:
now_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
print(f"\n{'='*20} 周期性检查开始 ({now_str}) {'='*20}")
tasks = get_combined_tasks()
if tasks:
print(f"📡 发现 {len(tasks)} 个符合条件且未跑过的任务,准备启动线程池...")
with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:
# 提交任务,将 address, time, username 传入
future_to_user = {
executor.submit(run_task, addr, info['time'], info['user']): info['user']
for addr, info in tasks.items()
}
for f in as_completed(future_to_user):
print(f.result())
else:
print("📭 暂无待处理任务 (所有账号已完成或 is_ok != 1)")
print(f"💤 本轮轮询结束,等待 {POLLING_INTERVAL//60} 分钟进行下次检查...")
time.sleep(POLLING_INTERVAL)
if __name__ == "__main__":
print("🌟 自动化调度服务已启动")
print(f"📂 配置文件: {TIME_FILE_PATH}")
print(f"⏱️ 轮询频率: {POLLING_INTERVAL}")
try:
monitor_center()
except KeyboardInterrupt:
print("\n👋 用户手动停止程序。")

View File

@@ -1,8 +0,0 @@
CDWZQ-2标-蓝家湾特大桥-24-30-平原
CDWZQ-2标-155号路基右线-461274-461570-155右-平原
CDWZQ-2标-龙骨湾右线大桥-0-7号墩-平原
CDWZQ-2标-区间路基135号-447663-447219-平原
CDWZQ-2标-区间路基152号小-458515-459000-152-1-平原
CDWZQ-2标-区间路基137-449311-449487-137-平原
CDWZQ-2标-155号路基左线新-461121-461570-2-平原
CDWZQ-2标-蓝家湾特大桥-31-31-平原

Binary file not shown.

View File

@@ -2,7 +2,7 @@
a = Analysis(
['scheduler_two.py'],
['scheduler.py'],
pathex=[],
binaries=[],
datas=[],
@@ -22,7 +22,7 @@ exe = EXE(
a.binaries,
a.datas,
[],
name='scheduler_two',
name='沉降观测自动上传',
debug=False,
bootloader_ignore_signals=False,
strip=False,