修改上传人员信息表为D盘固定位置;新增上传弹窗检查
This commit is contained in:
Binary file not shown.
13638
build/沉降观测自动上传/Analysis-00.toc
Normal file
13638
build/沉降观测自动上传/Analysis-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
6808
build/沉降观测自动上传/EXE-00.toc
Normal file
6808
build/沉降观测自动上传/EXE-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
6784
build/沉降观测自动上传/PKG-00.toc
Normal file
6784
build/沉降观测自动上传/PKG-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/沉降观测自动上传/PYZ-00.pyz
Normal file
BIN
build/沉降观测自动上传/PYZ-00.pyz
Normal file
Binary file not shown.
6873
build/沉降观测自动上传/PYZ-00.toc
Normal file
6873
build/沉降观测自动上传/PYZ-00.toc
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/沉降观测自动上传/base_library.zip
Normal file
BIN
build/沉降观测自动上传/base_library.zip
Normal file
Binary file not shown.
BIN
build/沉降观测自动上传/localpycs/pyimod01_archive.pyc
Normal file
BIN
build/沉降观测自动上传/localpycs/pyimod01_archive.pyc
Normal file
Binary file not shown.
BIN
build/沉降观测自动上传/localpycs/pyimod02_importers.pyc
Normal file
BIN
build/沉降观测自动上传/localpycs/pyimod02_importers.pyc
Normal file
Binary file not shown.
BIN
build/沉降观测自动上传/localpycs/pyimod03_ctypes.pyc
Normal file
BIN
build/沉降观测自动上传/localpycs/pyimod03_ctypes.pyc
Normal file
Binary file not shown.
BIN
build/沉降观测自动上传/localpycs/pyimod04_pywin32.pyc
Normal file
BIN
build/沉降观测自动上传/localpycs/pyimod04_pywin32.pyc
Normal file
Binary file not shown.
BIN
build/沉降观测自动上传/localpycs/struct.pyc
Normal file
BIN
build/沉降观测自动上传/localpycs/struct.pyc
Normal file
Binary file not shown.
347
build/沉降观测自动上传/warn-沉降观测自动上传.txt
Normal file
347
build/沉降观测自动上传/warn-沉降观测自动上传.txt
Normal 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)
|
||||
76459
build/沉降观测自动上传/xref-沉降观测自动上传.html
Normal file
76459
build/沉降观测自动上传/xref-沉降观测自动上传.html
Normal file
File diff suppressed because it is too large
Load Diff
BIN
build/沉降观测自动上传/沉降观测自动上传.pkg
Normal file
BIN
build/沉降观测自动上传/沉降观测自动上传.pkg
Normal file
Binary file not shown.
BIN
dist/scheduler_two.exe → dist/沉降观测自动上传.exe
vendored
BIN
dist/scheduler_two.exe → dist/沉降观测自动上传.exe
vendored
Binary file not shown.
Binary file not shown.
185
invalid_scheduler.py
Normal file
185
invalid_scheduler.py
Normal 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("🏁 所有并发任务处理序列结束。")
|
||||
5
main.py
5
main.py
@@ -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());
|
||||
|
||||
478
main_init.py
478
main_init.py
@@ -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)}")
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
music/901135.wav
BIN
music/901135.wav
Binary file not shown.
Binary file not shown.
@@ -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")
|
||||
@@ -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"))
|
||||
)
|
||||
save_upload_btn.click()
|
||||
self.logger.info("已点击保存上传按钮")
|
||||
|
||||
# 处理警告弹窗
|
||||
time.sleep(1)
|
||||
if not self.handle_warning_dialog():
|
||||
self.logger.error("处理警告弹窗失败")
|
||||
return False
|
||||
max_retries = 3
|
||||
for attempt in range(max_retries):
|
||||
save_upload_btn.click()
|
||||
self.logger.info(f"已点击保存上传按钮 (第 {attempt + 1} 次)")
|
||||
|
||||
return True
|
||||
# 处理警告弹窗
|
||||
time.sleep(1)
|
||||
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("加载用户数据失败")
|
||||
|
||||
288
scheduler.py
288
scheduler.py
@@ -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 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
|
||||
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_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
|
||||
|
||||
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"
|
||||
# }
|
||||
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}")
|
||||
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()
|
||||
|
||||
if not tasks_data:
|
||||
print("📭 接口未返回任何任务,程序退出。")
|
||||
return
|
||||
"""调度中心:每半小时检查一次"""
|
||||
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"🗂️ 发现 {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())
|
||||
print(f"💤 本轮轮询结束,等待 {POLLING_INTERVAL//60} 分钟进行下次检查...")
|
||||
time.sleep(POLLING_INTERVAL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🚀 自动化调度程序启动...")
|
||||
monitor_center()
|
||||
print("🏁 所有并发任务处理序列结束。")
|
||||
print("🌟 自动化调度服务已启动")
|
||||
print(f"📂 配置文件: {TIME_FILE_PATH}")
|
||||
print(f"⏱️ 轮询频率: {POLLING_INTERVAL}秒")
|
||||
try:
|
||||
monitor_center()
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 用户手动停止程序。")
|
||||
193
scheduler_two.py
193
scheduler_two.py
@@ -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👋 用户手动停止程序。")
|
||||
@@ -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-平原
|
||||
BIN
上传人员信息.xlsx
BIN
上传人员信息.xlsx
Binary file not shown.
@@ -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,
|
||||
Reference in New Issue
Block a user