Improvement

This commit is contained in:
Wang Zhiyu 2025-03-10 03:10:22 +08:00
commit afd1a37c0c
36 changed files with 1599 additions and 0 deletions

58
.gitigore Normal file
View File

@ -0,0 +1,58 @@
# Python cache files
__pycache__/
*.py[cod]
# Distribution / packaging
.Python
env/
venv/
ENV/
env.bak/
venv.bak/
*.egg-info/
dist/
build/
# IDEs and editors
.vscode/
.idea/
*.swp
*.swo
*.sublime-workspace
*.sublime-project
*pyc
# Jupyter Notebook
.ipynb_checkpoints
# Pytest
.cache
*.cover
*.log
nosetests.xml
coverage.xml
*.cover
*.py,cover
# MyPy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# C extensions
*.so
# Distribution files
*.egg
*.egg-info/
MANIFEST
# Other files
*.DS_Store
*.pdb
*.pyo
*.pyd
*.db
*.sqlite3

373
LICENSE Normal file
View File

@ -0,0 +1,373 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

2
README.md Normal file
View File

@ -0,0 +1,2 @@
# AiraPulsar 飞行控制系统
AiraPulsar (空中脉冲星, 简称 APSR) 是一套基于 WebSocket 的三点架构的轻量级网络飞行控制系统

4
comet/README.md Normal file
View File

@ -0,0 +1,4 @@
# Comet - 飞行器侧载端
Comet(彗星) 是 AiraPulsar 飞行控制系统的飞行器侧载端
Comet(飞行器) 通过 Moon(中继服务器) 进行通讯, 接受来自 Pulsar(控制器) 的指令

213
comet/agent_logs.log Normal file
View File

@ -0,0 +1,213 @@
2025-03-07T08:56:35.501146+0800 - INFO - 初始化代理对象
2025-03-07T08:56:35.501324+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T08:56:35.501414+0800 - INFO - 代理对象代号: comet
2025-03-07T08:57:13.903527+0800 - INFO - 初始化代理对象
2025-03-07T08:57:13.903678+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T08:57:13.903742+0800 - INFO - 代理对象代号: comet
2025-03-07T08:58:55.379859+0800 - INFO - 初始化代理对象
2025-03-07T08:58:55.380016+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T08:58:55.380084+0800 - INFO - 代理对象代号: comet
2025-03-07T08:59:05.381232+0800 - INFO - 初始化代理对象
2025-03-07T08:59:05.381387+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T08:59:05.381444+0800 - INFO - 代理对象代号: comet
2025-03-07T08:59:52.906831+0800 - INFO - 初始化代理对象
2025-03-07T08:59:52.906996+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T08:59:52.907060+0800 - INFO - 代理对象代号: comet
2025-03-07T09:00:41.008959+0800 - INFO - 初始化代理对象
2025-03-07T09:00:41.008959+0800 - INFO - 初始化代理对象
2025-03-07T09:00:41.009179+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:00:41.009179+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:00:41.009279+0800 - INFO - 代理对象代号: comet
2025-03-07T09:00:41.009279+0800 - INFO - 代理对象代号: comet
2025-03-07T09:00:44.632829+0800 - INFO - 初始化代理对象
2025-03-07T09:00:44.632829+0800 - INFO - 初始化代理对象
2025-03-07T09:00:44.633010+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:00:44.633010+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:00:44.633098+0800 - INFO - 代理对象代号: comet
2025-03-07T09:00:44.633098+0800 - INFO - 代理对象代号: comet
2025-03-07T09:01:31.329481+0800 - INFO - 初始化代理对象
2025-03-07T09:01:31.329481+0800 - INFO - 初始化代理对象
2025-03-07T09:01:31.329691+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:01:31.329691+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:01:31.329762+0800 - INFO - 代理对象代号: comet
2025-03-07T09:01:31.329762+0800 - INFO - 代理对象代号: comet
2025-03-07T09:02:18.294306+0800 - INFO - 初始化代理对象
2025-03-07T09:02:18.294306+0800 - INFO - 初始化代理对象
2025-03-07T09:02:18.294467+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:02:18.294467+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:02:18.294542+0800 - INFO - 代理对象代号: comet
2025-03-07T09:02:18.294542+0800 - INFO - 代理对象代号: comet
2025-03-07T09:02:49.424696+0800 - INFO - 初始化代理对象
2025-03-07T09:02:49.424696+0800 - INFO - 初始化代理对象
2025-03-07T09:02:49.424863+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:02:49.424863+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:02:49.424947+0800 - INFO - 代理对象代号: comet
2025-03-07T09:02:49.424947+0800 - INFO - 代理对象代号: comet
2025-03-07T09:03:12.117053+0800 - INFO - 初始化代理对象
2025-03-07T09:03:12.117053+0800 - INFO - 初始化代理对象
2025-03-07T09:03:12.117278+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:03:12.117278+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:03:12.117377+0800 - INFO - 代理对象代号: comet
2025-03-07T09:03:12.117377+0800 - INFO - 代理对象代号: comet
2025-03-07T09:03:43.470032+0800 - INFO - 初始化代理对象
2025-03-07T09:03:43.470032+0800 - INFO - 初始化代理对象
2025-03-07T09:03:43.470267+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:03:43.470267+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:03:43.470342+0800 - INFO - 代理对象代号: comet
2025-03-07T09:03:43.470342+0800 - INFO - 代理对象代号: comet
2025-03-07T09:11:06.876601+0800 - INFO - 初始化代理对象
2025-03-07T09:11:06.876601+0800 - INFO - 初始化代理对象
2025-03-07T09:11:06.876858+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:11:06.876858+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:11:06.876965+0800 - INFO - 代理对象代号: comet
2025-03-07T09:11:06.876965+0800 - INFO - 代理对象代号: comet
2025-03-07T09:12:16.848236+0800 - INFO - 初始化代理对象
2025-03-07T09:12:16.848236+0800 - INFO - 初始化代理对象
2025-03-07T09:12:16.848397+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:12:16.848397+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:12:16.848476+0800 - INFO - 代理对象代号: comet
2025-03-07T09:12:16.848476+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:10.751685+0800 - INFO - 初始化代理对象
2025-03-07T09:19:10.751685+0800 - INFO - 初始化代理对象
2025-03-07T09:19:10.751843+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:10.751843+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:10.751912+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:10.751912+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:20.013536+0800 - INFO - 初始化代理对象
2025-03-07T09:19:20.013536+0800 - INFO - 初始化代理对象
2025-03-07T09:19:20.013686+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:20.013686+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:20.013757+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:20.013757+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:39.079365+0800 - INFO - 初始化代理对象
2025-03-07T09:19:39.079365+0800 - INFO - 初始化代理对象
2025-03-07T09:19:39.079590+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:39.079590+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:19:39.079663+0800 - INFO - 代理对象代号: comet
2025-03-07T09:19:39.079663+0800 - INFO - 代理对象代号: comet
2025-03-07T09:20:13.855104+0800 - INFO - 初始化代理对象
2025-03-07T09:20:13.855104+0800 - INFO - 初始化代理对象
2025-03-07T09:20:13.855261+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:20:13.855261+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:20:13.855328+0800 - INFO - 代理对象代号: comet
2025-03-07T09:20:13.855328+0800 - INFO - 代理对象代号: comet
2025-03-07T09:20:22.574788+0800 - INFO - 初始化代理对象
2025-03-07T09:20:22.574788+0800 - INFO - 初始化代理对象
2025-03-07T09:20:22.574961+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:20:22.574961+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:20:22.575035+0800 - INFO - 代理对象代号: comet
2025-03-07T09:20:22.575035+0800 - INFO - 代理对象代号: comet
2025-03-07T09:26:57.804810+0800 - INFO - 初始化代理对象
2025-03-07T09:26:57.804810+0800 - INFO - 初始化代理对象
2025-03-07T09:26:57.804965+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:26:57.804965+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:26:57.805033+0800 - INFO - 代理对象代号: comet
2025-03-07T09:26:57.805033+0800 - INFO - 代理对象代号: comet
2025-03-07T09:28:26.670684+0800 - INFO - 初始化代理对象
2025-03-07T09:28:26.670684+0800 - INFO - 初始化代理对象
2025-03-07T09:28:26.670920+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:28:26.670920+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:28:26.671025+0800 - INFO - 代理对象代号: comet
2025-03-07T09:28:26.671025+0800 - INFO - 代理对象代号: comet
2025-03-07T09:28:26.674242+0800 - INFO - 成功建立连接
2025-03-07T09:28:26.674242+0800 - INFO - 成功建立连接
2025-03-07T09:28:35.156211+0800 - INFO - 初始化代理对象
2025-03-07T09:28:35.156211+0800 - INFO - 初始化代理对象
2025-03-07T09:28:35.156372+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:28:35.156372+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:28:35.156458+0800 - INFO - 代理对象代号: comet
2025-03-07T09:28:35.156458+0800 - INFO - 代理对象代号: comet
2025-03-07T09:28:35.159041+0800 - INFO - 成功建立连接
2025-03-07T09:28:35.159041+0800 - INFO - 成功建立连接
2025-03-07T09:29:25.100332+0800 - INFO - 初始化代理对象
2025-03-07T09:29:25.100332+0800 - INFO - 初始化代理对象
2025-03-07T09:29:25.100478+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:29:25.100478+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:29:25.100548+0800 - INFO - 代理对象代号: comet
2025-03-07T09:29:25.100548+0800 - INFO - 代理对象代号: comet
2025-03-07T09:29:25.102459+0800 - INFO - 成功建立连接
2025-03-07T09:29:25.102459+0800 - INFO - 成功建立连接
2025-03-07T09:30:13.965473+0800 - INFO - 初始化代理对象
2025-03-07T09:30:13.965473+0800 - INFO - 初始化代理对象
2025-03-07T09:30:13.965675+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:30:13.965675+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:30:13.965765+0800 - INFO - 代理对象代号: comet
2025-03-07T09:30:13.965765+0800 - INFO - 代理对象代号: comet
2025-03-07T09:30:13.967862+0800 - INFO - 成功建立连接
2025-03-07T09:30:13.967862+0800 - INFO - 成功建立连接
2025-03-07T09:31:20.560819+0800 - INFO - 初始化代理对象
2025-03-07T09:31:20.560819+0800 - INFO - 初始化代理对象
2025-03-07T09:31:20.561346+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:31:20.561346+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:31:20.561512+0800 - INFO - 代理对象代号: comet
2025-03-07T09:31:20.561512+0800 - INFO - 代理对象代号: comet
2025-03-07T09:31:20.564821+0800 - INFO - 成功建立连接
2025-03-07T09:31:20.564821+0800 - INFO - 成功建立连接
2025-03-07T09:31:41.871167+0800 - INFO - 初始化代理对象
2025-03-07T09:31:41.871167+0800 - INFO - 初始化代理对象
2025-03-07T09:31:41.871318+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:31:41.871318+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T09:31:41.871391+0800 - INFO - 代理对象代号: comet
2025-03-07T09:31:41.871391+0800 - INFO - 代理对象代号: comet
2025-03-07T09:31:41.873516+0800 - INFO - 成功建立连接
2025-03-07T09:31:41.873516+0800 - INFO - 成功建立连接
2025-03-07T10:02:22.246919+0800 - INFO - 初始化代理对象
2025-03-07T10:02:22.246919+0800 - INFO - 初始化代理对象
2025-03-07T10:02:22.247153+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T10:02:22.247153+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T10:02:22.247261+0800 - INFO - 代理对象代号: comet
2025-03-07T10:02:22.247261+0800 - INFO - 代理对象代号: comet
2025-03-07T10:02:22.247736+0800 - INFO - 等待连接成功
2025-03-07T10:02:22.247736+0800 - INFO - 等待连接成功
2025-03-07T10:02:22.249670+0800 - INFO - 成功建立连接
2025-03-07T10:02:22.249670+0800 - INFO - 成功建立连接
2025-03-07T10:02:22.347928+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T10:02:22.347928+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:55:55.862879+0800 - INFO - 初始化代理对象
2025-03-07T15:55:55.862879+0800 - INFO - 初始化代理对象
2025-03-07T15:55:55.863020+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:55:55.863020+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:55:55.863103+0800 - INFO - 代理对象代号: comet
2025-03-07T15:55:55.863103+0800 - INFO - 代理对象代号: comet
2025-03-07T15:55:55.863455+0800 - INFO - 等待连接成功
2025-03-07T15:55:55.863455+0800 - INFO - 等待连接成功
2025-03-07T15:55:55.865329+0800 - INFO - 成功建立连接, 单向延迟 7.05718994140625e-05ms
2025-03-07T15:55:55.865329+0800 - INFO - 成功建立连接, 单向延迟 7.05718994140625e-05ms
2025-03-07T15:55:55.963668+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:55:55.963668+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:56:29.263974+0800 - INFO - 初始化代理对象
2025-03-07T15:56:29.263974+0800 - INFO - 初始化代理对象
2025-03-07T15:56:29.264145+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:56:29.264145+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:56:29.264238+0800 - INFO - 代理对象代号: comet
2025-03-07T15:56:29.264238+0800 - INFO - 代理对象代号: comet
2025-03-07T15:56:29.264733+0800 - INFO - 等待连接成功
2025-03-07T15:56:29.264733+0800 - INFO - 等待连接成功
2025-03-07T15:56:29.266574+0800 - INFO - 成功建立连接, 单向延迟 0ms
2025-03-07T15:56:29.266574+0800 - INFO - 成功建立连接, 单向延迟 0ms
2025-03-07T15:56:29.364981+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:56:29.364981+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:56:47.266556+0800 - INFO - 初始化代理对象
2025-03-07T15:56:47.266556+0800 - INFO - 初始化代理对象
2025-03-07T15:56:47.266726+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:56:47.266726+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T15:56:47.266820+0800 - INFO - 代理对象代号: comet
2025-03-07T15:56:47.266820+0800 - INFO - 代理对象代号: comet
2025-03-07T15:56:47.267230+0800 - INFO - 等待连接成功
2025-03-07T15:56:47.267230+0800 - INFO - 等待连接成功
2025-03-07T15:56:47.269156+0800 - INFO - 成功建立连接, 单向延迟 0.1ms
2025-03-07T15:56:47.269156+0800 - INFO - 成功建立连接, 单向延迟 0.1ms
2025-03-07T15:56:47.367454+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T15:56:47.367454+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T17:45:08.362971+0800 - INFO - 初始化代理对象
2025-03-07T17:45:08.362971+0800 - INFO - 初始化代理对象
2025-03-07T17:45:08.363174+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T17:45:08.363174+0800 - INFO - 卫星地址: ws://127.0.0.1:8765
2025-03-07T17:45:08.363287+0800 - INFO - 代理对象代号: comet
2025-03-07T17:45:08.363287+0800 - INFO - 代理对象代号: comet
2025-03-07T17:45:08.363894+0800 - INFO - 等待连接成功
2025-03-07T17:45:08.363894+0800 - INFO - 等待连接成功
2025-03-07T17:45:08.365733+0800 - INFO - 成功建立连接, 单向延迟 0.1ms
2025-03-07T17:45:08.365733+0800 - INFO - 成功建立连接, 单向延迟 0.1ms
2025-03-07T17:45:08.464116+0800 - INFO - 连接成功, 取消阻塞
2025-03-07T17:45:08.464116+0800 - INFO - 连接成功, 取消阻塞

19
comet/comet.py Normal file
View File

@ -0,0 +1,19 @@
from lib import agency
import yaml
import time
moon_addr = None
moon_port = None
with open('comet_config.yaml', 'r') as config_file:
config = yaml.safe_load(config_file)
moon_addr = config.get("moon_addr", "127.0.0.1")
port = config.get("moon_port", 8765)
agent = None
def handler(data: dict):
pass
if __name__ == "__main__":
agent = agency.Agent(name="comet", addr=moon_addr, handler=handler)
agent.runasthread()

1
comet/comet_config.yaml Normal file
View File

@ -0,0 +1 @@
moon_addr: "ws://127.0.0.1:8765"

View File

@ -0,0 +1,33 @@
class Motor(object):
angle = None
def __init__(self, id):
self.angle = 0 # mark
self.id = id
def get(self):
return self.angle
def turn(self, add_angle):
self.angle += add_angle
return self.angle
class Engine(object):
speed = None
def __init__(self, id):
self.speed = 0 # Mark
self.id = id
def get(self):
return self.speed
def tune(self, new_speed):
self.speed = new_speed
return self.speed
class Battery:
def get():
power_left = 0.9
return power_left
def stat():
status = "Unplugged"
return status
class Network:
def stat():
delay = 0.2 # ms
return delay # or -1 (unreachable)

202
comet/hal/tuner.py Normal file
View File

@ -0,0 +1,202 @@
"""
filename: tuner.py
"""
from bus import interface
import threading
class Surface(object):
motor = None
name = None
curr_angle = None
id = None
type_ = "surface"
def __init__(self, name, id):
self.motor = interface.Motor(id)
self.curr_angle = self.motor.get()
self.name = name
self.id = id
print("初始化操纵面 {}, 硬件代号绑定为 {}".format(name, id))
def add_angle(self, angle):
self.motor.turn(angle)
self.curr_angle = self.motor.get();
#print("将 {} 的角度加成 {} 度".format(self.name, angle))
def set_angle(self, angle):
angle_turn = angle - self.motor.get()
self.motor.turn(angle_turn)
self.curr_angle = self.motor.get()
#print("将 {} 的角度设置为 {} 度".format(self.name, angle))
def get_angle(self):
self.curr_angle = self.motor.get()
#print("{} 当前角度为 {} 度".format(self.name, self.curr_angle))
return self.curr_angle
def status(self):
return {"type":"Surface", "name":self.name, "angle":self.curr_angle}
def selfchk(self):
print("开始自检操纵面 {}".format(self.name))
self.add_angle(30)
self.add_angle(-60)
self.set_angle(0)
class Engine(object):
engine = None
curr_speed = None
id = None
name = None
type_ = "engine"
def __init__(self, name, id):
self.id = id
self.name = name
self.engine = interface.Engine(id)
self.curr_speed = self.engine.get()
print("初始化引擎 {}, 硬件代号绑定为 {}".format(name, id))
def set_speed(self, new_speed):
self.engine.tune(new_speed)
self.curr_speed = self.engine.get()
##print("将 {} 的转速调谐为 {} RPM".format(self.name, new_speed))
def add_speed(self, add_speed):
self.engine.tune(self.curr_speed + add_speed)
self.curr_speed = self.engine.get()
##print("将 {} 的转速加成 {} RPM".format(self.name, add_speed))
def get_speed(self):
##print("{} 当前转速为 {} RPM".format(self.name, self.curr_speed))
self.curr_speed = self.engine.get()
return self.curr_speed
def status(self):
##print(self.curr_speed)
return {"type":"Engine", "name":self.name, "speed":self.get_speed()}
def selfchk(self):
print("开始自检引擎 {}".format(self.name))
self.add_speed(3)
self.add_speed(-6)
self.set_speed(0)
self.curr_speed = self.engine.get()
class Sensor:
data = dict()
data["speed"] = 0
data["battery"] = 0
data["xangle"] = 0
data["yangle"] = 0
data["zangle"] = 0
data["sign"] = 0
data["cpuload"] = 0
data["memload"] = 0
data["memsum"] = 0
data["torch"] = 0
data["acc"] = 0
is_torch_on = 0
@staticmethod
def speed():
Sensor.data["speed"] = 99
return Sensor.data["speed"]
@staticmethod
def battery():
Sensor.data["battery"] = 0.8
return Sensor.data["battery"]
@staticmethod
def xangle():
Sensor.data["xangle"] = 201
return Sensor.data["xangle"]
@staticmethod
def yangle():
Sensor.data["yangle"] = 108
return Sensor.data["yangle"]
@staticmethod
def zangle():
Sensor.data["zangle"] = 0
return Sensor.data["zangle"]
@staticmethod
def sign():
Sensor.data["sign"] = 322
return Sensor.data["sign"]
@staticmethod
def cpuload():
Sensor.data["cpuload"] = 0.12
return Sensor.data["cpuload"]
@staticmethod
def memload():
Sensor.data["memload"] = 0.88
return Sensor.data["memload"]
@staticmethod
def memsum():
Sensor.data["memsum"] = 2048
return Sensor.data["memsum"]
@staticmethod
def torch():
Sensor.data["torch"] = Sensor.is_torch_on
return Sensor.data["torch"]
@staticmethod
def torchon():
Sensor.is_torch_on = 1
@staticmethod
def acc():
Sensor.data["acc"] = 3
return Sensor.data["acc"]
@staticmethod
def net_delay():
Sensor.data["net_delay"] = 300
return Sensor.data["net_delay"]
@staticmethod
def refresh():
Sensor.speed()
Sensor.battery()
Sensor.xangle()
Sensor.yangle()
Sensor.zangle()
Sensor.sign()
Sensor.cpuload()
Sensor.memload()
Sensor.memsum()
Sensor.torch()
Sensor.acc()
Sensor.net_delay()
@staticmethod
def stat():
#Sensor.refresh()
return Sensor.data
def pseudo_gui():
import tkinter as tk
def update_label(key, value):
labels[key].config(text=f"{key}: {value}")
def update():
while 1:
for key in Sensor.data.keys():
Sensor.data[key] = sliders[key].get()
root = tk.Tk()
root.title("DEBUGGING CONSOLE")
labels = {}
sliders = {}
# 自动布局
for key in Sensor.data.keys():
# 创建标签
label = tk.Label(root, text=f"{key}: {Sensor.data[key]}")
label.pack()
labels[key] = label
# 创建滑块
slider = tk.Scale(root, from_=0, to=100, orient=tk.HORIZONTAL, command=lambda value, k=key: update_label(k, value))
slider.pack()
sliders[key] = slider
up = threading.Thread(target=update, name='Update')
up.start()
root.mainloop()
up.join()

1
comet/lib Symbolic link
View File

@ -0,0 +1 @@
../lib

View File

@ -0,0 +1,36 @@
surface:
-
id: al
attr: "a"
side: "l"
desc: 左副翼
-
id: ar
attr: "a"
side: "r"
desc: 右副翼
-
id: fl
attr: "f"
side: "l"
desc: 左襟翼
-
id: fr
attr: "f"
side: "r"
desc: 右襟翼
-
id: r
attr: "h"
side: "n"
desc: 方向舵
-
id: e
attr: "i"
side: "n"
desc: 升降舵
engine:
-
id: eng_1
desc: "主引擎"
vector: False

View File

@ -0,0 +1,3 @@
name: "jf_17_pm"
desc: "枭龙 单引擎单垂尾常规布局航模 螺旋桨驱动 尺寸修改型"
view: ./src/view.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

98
lib/agency.py Normal file
View File

@ -0,0 +1,98 @@
"""
filename: agency.py
description: WebSocket network agency for AiraPulsar components.
This module serves as a universal client module for Moons,
facilitating communication between Pulsar/Comet and Moons.
"""
import websocket
import json
from loguru import logger
import threading
import time
# 配置日志
logger.add("agent_logs.log", rotation="1 MB", level="INFO", format="{time} - {level} - {message}")
class Agent(object):
"""
handler: function which processes args
receiver: sendto
emitter: sendfrom
content: original string data
data: formatted json data -> dict
"""
wsapp = None
is_connected = 0
def __init__(self, name, addr, handler):
logger.add("agent_logs.log", rotation="1 MB", level="INFO", format="{time} - {level} - {message}")
logger.info("初始化代理对象")
logger.info(f"卫星地址: {addr}")
logger.info(f"代理对象代号: {name}")
self.name = name
self.addr = addr
self.handler = handler
self.wsapp = websocket.WebSocketApp(
f"{addr}",
on_open=self.__on_open,
on_message=self.__on_message,
on_close=self.__on_close
)
def __dumper(self, receiver: str, content: dict):
msg = {
"id": self.name,
"receiver": receiver,
"content": content
}
return msg
def __loader(self, received_data: str):
try:
data = json.loads(received_data)
content = data["content"]
emitter = data["emitter"]
return {"content": content, "emitter": emitter}
except json.JSONDecodeError:
logger.error("接收到的消息不是有效的 JSON 格式")
return {"content": None, "emitter": None}
def __on_open(self, wsapp):
start_time = time.time()
self.send(receiver="auth", data="auth")
self.is_connected = True
end_time = time.time()
delta = end_time - start_time
logger.info(f"成功建立连接, 单向延迟 {round(delta*10000)/10}ms")
def __on_message(self, wsapp, message):
loaded_data = self.__loader(message)
if loaded_data["emitter"] is not None and loaded_data["content"] is not None:
self.handler(emitter=loaded_data["emitter"],
content=loaded_data["content"])
def __on_close(self, wsapp):
self.is_connected = False
logger.info("WebSocket 连接已关闭")
def send(self, receiver: str, data):
if isinstance(data, dict):
self.wsapp.send(
json.dumps(self.__dumper(receiver, data))
)
elif isinstance(data, str):
self.wsapp.send(
json.dumps(self.__dumper(receiver, data))
)
else:
logger.warning("数据类型不支持")
def run(self):
self.wsapp.run_forever()
def runasthread(self):
thread = threading.Thread(target=self.wsapp.run_forever)
thread.start()
logger.info("等待连接成功")
while self.is_connected == 0:
time.sleep(0.1)
logger.info("连接成功, 取消阻塞")

13
moon/README.md Normal file
View File

@ -0,0 +1,13 @@
# Moon - 中继服务器
Moon(卫星) 是 AiraPulsar 飞行控制系统的中继服务器
Comet(飞行器) 与 Pulsar(控制器) 通过 Moon(中继服务器) 进行通讯
这样做的好处:
- 无需设置第三方内网穿透
- 轻松查阅连接日志
- 可扩展至多端远程架构
- 仅需一个地址即可完成通讯
- 便于第三方介入
建议将 Moon 部署至公网环境服务器中

1
moon/lib Symbolic link
View File

@ -0,0 +1 @@
../lib

119
moon/logs.log Normal file
View File

@ -0,0 +1,119 @@
2025-03-07T08:44:59.049714+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T08:54:33.168394+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T08:56:35.504311+0800 - INFO - 新客户端连接: 1
2025-03-07T08:57:12.572151+0800 - INFO - 客户端断开连接: 1
2025-03-07T08:57:13.906086+0800 - INFO - 新客户端连接: 2
2025-03-07T08:57:18.478193+0800 - INFO - 客户端断开连接: 2
2025-03-07T08:58:55.383088+0800 - INFO - 新客户端连接: 3
2025-03-07T08:59:04.634280+0800 - INFO - 客户端断开连接: 3
2025-03-07T08:59:05.383848+0800 - INFO - 新客户端连接: 4
2025-03-07T08:59:51.560133+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T08:59:52.909275+0800 - INFO - 新客户端连接: 1
2025-03-07T08:59:55.915998+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:00:43.283319+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:00:44.635715+0800 - INFO - 新客户端连接: 1
2025-03-07T09:01:29.896734+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:01:31.332525+0800 - INFO - 新客户端连接: 1
2025-03-07T09:02:17.026957+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:02:18.296634+0800 - INFO - 新客户端连接: 1
2025-03-07T09:02:48.143588+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:02:49.427049+0800 - INFO - 新客户端连接: 1
2025-03-07T09:03:10.834981+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:03:12.119704+0800 - INFO - 新客户端连接: 1
2025-03-07T09:03:12.119986+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:03:12.120071+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:03:42.168476+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:03:43.472305+0800 - INFO - 新客户端连接: 1
2025-03-07T09:03:43.472547+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:11:05.352860+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:11:06.882419+0800 - INFO - 新客户端连接: 1
2025-03-07T09:11:06.882773+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:12:14.693477+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:12:16.851004+0800 - INFO - 新客户端连接: 1
2025-03-07T09:12:16.851656+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:19:09.007145+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:19:10.754140+0800 - INFO - 新客户端连接: 1
2025-03-07T09:19:10.754434+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:19:18.493238+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:19:20.016578+0800 - INFO - 新客户端连接: 1
2025-03-07T09:19:20.016862+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:19:37.700993+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:19:39.081530+0800 - INFO - 新客户端连接: 1
2025-03-07T09:19:39.081796+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:19:43.080448+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:19:43.080720+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:20:12.487057+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:20:13.857585+0800 - INFO - 新客户端连接: 1
2025-03-07T09:20:13.857880+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:20:14.799728+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:20:17.208026+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:20:22.577076+0800 - INFO - 新客户端连接: 1
2025-03-07T09:20:22.577489+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:20:23.575751+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:20:23.576044+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:26:56.246176+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:26:57.807128+0800 - INFO - 新客户端连接: 1
2025-03-07T09:26:57.807419+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:26:58.805874+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:26:58.806066+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:28:25.117434+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:28:26.673876+0800 - INFO - 新客户端连接: 1
2025-03-07T09:28:26.674262+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:28:27.671936+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:28:27.672177+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:28:29.778081+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:28:33.464911+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:28:35.158721+0800 - INFO - 新客户端连接: 1
2025-03-07T09:28:35.159064+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:28:36.157310+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:28:36.157514+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:29:18.205614+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:29:22.053522+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:29:25.102228+0800 - INFO - 新客户端连接: 1
2025-03-07T09:29:25.102476+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:29:26.101162+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": "hi"}
2025-03-07T09:29:26.101351+0800 - ERROR - 无法解码 JSON 消息
2025-03-07T09:30:12.603632+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:30:13.967524+0800 - INFO - 新客户端连接: 1
2025-03-07T09:30:13.967874+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:30:14.966548+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": {"hi": "hi"}}
2025-03-07T09:30:14.966787+0800 - INFO - 客户端断开连接: 1
2025-03-07T09:31:19.129916+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:31:20.564382+0800 - INFO - 新客户端连接: 1
2025-03-07T09:31:20.564841+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:31:21.562523+0800 - INFO - 处理信息: {"id": "comet", "receiver": "1", "content": {"hi": "hi"}}
2025-03-07T09:31:21.562718+0800 - WARNING - 无法发送至端点
2025-03-07T09:31:31.503846+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T09:31:41.873175+0800 - INFO - 新客户端连接: 1
2025-03-07T09:31:41.873532+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T09:31:42.872072+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
2025-03-07T09:31:42.872284+0800 - WARNING - 无法发送至端点
2025-03-07T09:31:42.872356+0800 - WARNING - 无法发送至端点
2025-03-07T10:02:07.173456+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T10:02:22.249420+0800 - INFO - 新客户端连接: 1
2025-03-07T10:02:22.249683+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T10:02:22.348277+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
2025-03-07T10:02:22.348428+0800 - WARNING - 无法发送至端点
2025-03-07T10:02:22.348504+0800 - WARNING - 无法发送至端点
2025-03-07T15:55:54.389580+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T15:55:55.865014+0800 - INFO - 新客户端连接: 1
2025-03-07T15:55:55.865334+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T15:55:55.964049+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
2025-03-07T15:55:55.964208+0800 - WARNING - 无法发送至端点
2025-03-07T15:55:55.964276+0800 - WARNING - 无法发送至端点
2025-03-07T15:55:58.304575+0800 - INFO - 客户端断开连接: 1
2025-03-07T15:56:27.769208+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T15:56:29.266347+0800 - INFO - 新客户端连接: 1
2025-03-07T15:56:29.266586+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T15:56:29.365433+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
2025-03-07T15:56:29.365581+0800 - WARNING - 无法发送至端点
2025-03-07T15:56:29.365643+0800 - WARNING - 无法发送至端点
2025-03-07T15:56:45.972404+0800 - INFO - 启动"卫星"中继服务器: ws://0.0.0.0:8765
2025-03-07T15:56:47.268864+0800 - INFO - 新客户端连接: 1
2025-03-07T15:56:47.269164+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}
2025-03-07T15:56:47.367838+0800 - INFO - 处理信息: {"id": "comet", "receiver": "*", "content": {"hi": "hi"}}
2025-03-07T15:56:47.367979+0800 - WARNING - 无法发送至端点
2025-03-07T15:56:47.368041+0800 - WARNING - 无法发送至端点
2025-03-07T17:45:07.183410+0800 - INFO - 客户端断开连接: 1
2025-03-07T17:45:08.365484+0800 - INFO - 新客户端连接: 2
2025-03-07T17:45:08.365737+0800 - INFO - 处理信息: {"id": "comet", "receiver": "auth", "content": "auth"}

79
moon/moon.py Normal file
View File

@ -0,0 +1,79 @@
import json
from loguru import logger
import yaml
from websocket_server import WebsocketServer
import os
# 设置当前工作目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# 配置日志
logger.add("logs.log", rotation="1 MB", level="INFO", format="{time} - {level} - {message}")
moon_addr = None
port = None
with open('moon_config.yaml', 'r') as config_file:
config = yaml.safe_load(config_file)
moon_addr = config.get("addr", "127.0.0.1")
port = config.get("port", 8765)
clients = dict() # id -> name
clients_rev = dict() # name -> id
clients_connection = dict() # id -> client obj
def new_client(client, server):
clients_connection[client["id"]] = client
logger.info(f"新客户端连接: {client['id']}")
def client_left(client, server):
logger.info(f"客户端断开连接: {client['id']}")
# 连接关闭时从集合中移除
if client in clients.values():
clients.pop(clients_rev[client['id']])
clients_rev.pop(client['id'])
def message_received(client, server, message):
logger.info(f"处理信息: {message}")
try:
msg = json.loads(message)
clients[msg["id"]] = client['id']
clients_rev[client['id']] = msg["id"]
if msg["receiver"] == "auth":
return
if msg["receiver"] == "*":
try:
for i in clients_rev.values():
content = msg["content"]
content["emitter"] = msg["id"]
server.send_message(
clients_connection[i],
json.dumps(content))
except KeyError:
logger.warning("无法发送至端点")
try:
content = msg["content"]
content["emitter"] = msg["id"]
server.send_message(
clients_connection[clients[msg["receiver"]]],
json.dumps(content))
except KeyError:
logger.warning("无法发送至端点")
except json.JSONDecodeError:
logger.error("无法解码 JSON 消息")
def main():
global moon_addr
global port
server = WebsocketServer(host=moon_addr, port=port)
server.set_fn_new_client(new_client)
server.set_fn_client_left(client_left)
server.set_fn_message_received(message_received)
logger.info(f'启动"卫星"中继服务器: ws://{moon_addr}:{port}')
server.run_forever()
if __name__ == "__main__":
main()

2
moon/moon_config.yaml Normal file
View File

@ -0,0 +1,2 @@
addr: 0.0.0.0
port: 8765

59
pulsar/1.py Normal file
View File

@ -0,0 +1,59 @@
import pygame
import sys
# 初始化 Pygame
pygame.init()
# 设置窗口大小
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Pygame Draw Example")
# 定义颜色
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
# 主循环
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 填充背景
screen.fill(WHITE)
# 绘制线条
pygame.draw.line(screen, RED, (50, 50), (200, 50), 5)
pygame.draw.aaline(screen, BLUE, (50, 100), (200, 100))
# 绘制矩形
pygame.draw.rect(screen, GREEN, (50, 150, 150, 100), 0) # 填充矩形
pygame.draw.rect(screen, BLACK, (250, 150, 150, 100), 5) # 边框矩形
# 绘制圆形
pygame.draw.circle(screen, YELLOW, (400, 200), 50, 0) # 填充圆形
pygame.draw.circle(screen, BLACK, (500, 200), 50, 5) # 边框圆形
# 绘制椭圆
pygame.draw.ellipse(screen, BLUE, (50, 300, 200, 100), 0) # 填充椭圆
pygame.draw.ellipse(screen, BLACK, (300, 300, 200, 100), 5) # 边框椭圆
# 绘制多边形
points = [(600, 400), (700, 300), (800, 400), (700, 500)]
pygame.draw.polygon(screen, GREEN, points, 0) # 填充多边形
pygame.draw.polygon(screen, BLACK, points, 5) # 边框多边形
# 绘制弧
pygame.draw.arc(screen, RED, (50, 450, 200, 100), 0, 3.14, 5) # 弧
# 更新显示
pygame.display.flip()
# 退出 Pygame
pygame.quit()
sys.exit()

4
pulsar/README.md Normal file
View File

@ -0,0 +1,4 @@
# Pulsar - 控制器侧载端
Pulsar(脉冲星) 是 AiraPulsar 飞行控制系统的控制器侧载端, 项目亦由此而得名
Pulsar(控制器) 通过 Moon(中继服务器) 进行通讯, 给 Comet(飞行器) 发送指令

43
pulsar/graph.py Normal file
View File

@ -0,0 +1,43 @@
import pygame
class Graph:
def rect(frame, position: tuple = (0, 0), size: tuple = (1, 1), color: tuple = (255, 255, 255), width: int = 0):
pygame.draw.rect(frame.surface, color=color, rect=(position[0], position[1], size[0], size[1]), width=width)
def line(frame, start_pos: tuple, end_pos: tuple, color: tuple = (255, 255, 255)):
pygame.draw.aaline(frame.surface, color, start_pos, end_pos)
def circle(frame, center: tuple, radius: int, color: tuple = (255, 255, 255), width: int = 0):
pygame.draw.circle(frame.surface, color, center, radius, width)
def ellipse(frame, rect: tuple, color: tuple = (255, 255, 255), width: int = 0):
pygame.draw.ellipse(frame.surface, color, rect, width)
def polygon(frame, pointlist: list, color: tuple = (255, 255, 255), width: int = 0):
pygame.draw.polygon(frame.surface, color, pointlist, width)
def arc(frame, rect: tuple, color: tuple = (255, 255, 255), start_angle: float = 0, stop_angle: float = 3.14, width: int = 1):
pygame.draw.arc(frame.surface, color, rect, start_angle, stop_angle, width)
def points(frame, pos: tuple, color: tuple = (255, 255, 255)):
pygame.draw.point(frame.surface, color, pos)
def lines(frame, pointlist: list, color: tuple = (255, 255, 255), closed: bool = False):
pygame.draw.aalines(frame.surface, color, closed, pointlist)
class Frame(object):
name = None
size = None
surface = None
is_hide = False
def __init__(self, name: str, size: tuple):
self.name = name
self.surface = pygame.Surface(size, flags=pygame.HWSURFACE)
print("初始化子模块")
def show(self, window, position: tuple):
if not self.is_hide:
window.blit(self.surface, position)
def set_visible(self, newstat=True):
self.is_hide = newstat
def set_position(self, newposition):
self.position = newposition

1
pulsar/lib Symbolic link
View File

@ -0,0 +1 @@
../lib

3
testfield/README.md Normal file
View File

@ -0,0 +1,3 @@
# 实验与参考用代码
仅作技术分析验证与历史留存, 若要修改此项目代码, 不必处理此文件夹
若你要使用此软件, 可以克隆后直接将 testfield 文件夹删除

View File

@ -0,0 +1,2 @@
# Sin 波形传输 [成功]
基于 websocket 的通讯功能模型测试

View File

@ -0,0 +1,70 @@
import pygame
import sys
import threading
import math
import websocket
import json
import time
sin_remote = 0
th1 = None
th2 = None
moon_addr = "127.0.0.1"
def on_open(wsapp):
print("打开新连接")
print("准备待命")
message = {
"id": "comet",
"sendto": "auth",
"content": "none"
}
wsapp.send(json.dumps(message))
def on_message(wsapp, message):
global sin_remote
print(f"接收到消息: {message}")
if message == "-3":
th1.start()
sin_remote = float(message)
def on_close(wsapp):
print("on_close")
def inet():
global sin_remote
wsapp = websocket.WebSocketApp(f"ws://{moon_addr}:8765",
on_open=on_open,
on_message=on_message,
on_close=on_close)
wsapp.run_forever()
def grap():
pygame.init()
width, height = 628, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("TEST")
LOCAL_COLOR = (255, 255, 255, 64)
REMOTE_COLOR = [(0, 0, 255, 128), (255, 0, 0, 128)]
frequency = 0.01 # 波动频率
amplitude = 100 # 波动幅度
x = round(time.time()*1000)
while True:
x = round(time.time()*1000)
x = x % 628
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
#pygame.draw.rect(screen, (0, 0, 0), (x, 0, 5, height)) # 扫描线
y = height // 2 + amplitude * math.sin((x * frequency))
y_remote = height // 2 + amplitude * sin_remote
pygame.draw.rect(screen, (round(time.time()*1000+120)%256,round(time.time()*1000)%256,round(time.time()*1000+60)%256), (x, int(y_remote), 4, 4)) # 远程波形
pygame.draw.rect(screen, LOCAL_COLOR, (x, int(y), 2, 2)) # 本地波形
pygame.display.flip()
pygame.time.delay(10)
if __name__ == "__main__":
th1 = threading.Thread(target=grap)
th2 = threading.Thread(target=inet)
# 启动线程
th2.start()

View File

@ -0,0 +1,44 @@
import json
from websocket_server import WebsocketServer
moon_addr = "127.0.0.1"
clients = dict() # id -> name
clients_rev = dict() # name -> id
clients_connection = dict() # id -> client obj
def new_client(client, server):
clients_connection[client["id"]] = client
print(f"新客户端连接: {client['id']}")
def client_left(client, server):
print(f"客户端断开连接: {client['id']}")
# 连接关闭时从集合中移除
if client in clients.values():
clients.pop(clients_rev[client['id']])
clients_rev.pop(client['id'])
def message_received(client, server, message):
print(f"处理信息: {message}")
try:
msg = json.loads(message)
clients[msg["id"]] = client['id']
clients_rev[client['id']] = msg["id"]
if msg["sendto"] == "auth":
return
try:
server.send_message(clients_connection[clients[msg["sendto"]]], msg["content"])
except KeyError:
print("无法发送至端点")
except json.JSONDecodeError:
print("无法解码 JSON 消息")
def main():
server = WebsocketServer(host=f'{moon_addr}', port=8765)
server.set_fn_new_client(new_client)
server.set_fn_client_left(client_left)
server.set_fn_message_received(message_received)
print(f'启动"卫星"中继服务器: ws://{moon_addr}:8765')
server.run_forever()
if __name__ == "__main__":
main()

View File

@ -0,0 +1,34 @@
import websocket
import threading
import math
import json
import time
moon_addr = "127.0.0.1"
def on_open(wsapp):
print("打开新的连接")
input("回车以启动同步: ")
message = {
"id": "pulsar",
"sendto": "comet",
"content": "-3"
}
wsapp.send(json.dumps(message))
x = round(time.time()*1000)
while 1:
x = round(time.time()*1000)
x = x % 628
message["content"] = str(math.sin(x*0.01))
wsapp.send(json.dumps(message))
time.sleep(0.01)
def on_message(wsapp, message):
print("on_message:", message)
def on_close(wsapp):
print("on_close")
wsapp = websocket.WebSocketApp(f"ws://{moon_addr}:8765",
on_open=on_open,
on_message=on_message,
on_close=on_close)
wsapp.run_forever()

View File

@ -0,0 +1,2 @@
# 总览测试
基于 bmp 的通讯功能模型测试

63
testfield/visual/main.py Normal file
View File

@ -0,0 +1,63 @@
from PIL import Image
import pygame
import sys
import time
colorzone = dict()
def img():
# 打开 BMP 文件
bmp_image = Image.open('view.bmp')
# 获取图像的基本信息
width, height = bmp_image.size
mode = bmp_image.mode
print(f"图像宽度: {width}, 高度: {height}, 模式: {mode}")
# 读取图像数据
pixel_data = bmp_image.load() # 获取像素数据
# 遍历每个像素并打印其 RGB 值
minleft = 0x3f3f3f3f
mintop = 0x3f3f3f3f
for y in range(height):
for x in range(width):
minleft = min(minleft, x)
mintop = min(mintop, y)
r, g, b = pixel_data[x, y] # 获取每个像素的 RGB 值
if r == 255 and b == 255 and g == 255:
pass
else:
if (r,g,b) in colorzone:
colorzone[(r,g,b)].append(((x//8)*4,(y//8)*4))
else:
colorzone[(r,g,b)] = list()
colorzone[(r,g,b)].append((x//8*4,y//8*4))
#print(f"像素 ({x}, {y}): R={r}, G={g}, B={b}")
colorzone[(255,255,255)] = colorzone[(0,0,0)]
# 关闭图像
bmp_image.close()
def grap():
pygame.init()
width, height = 800, 1000
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("TEST")
global colorzone
while True:
for i in colorzone.keys():
for j in colorzone[i]:
if i != (255,255,255):
pygame.draw.rect(screen, i, (j[0], j[1], 2, 2)) # 远程波形
else:
pygame.draw.rect(screen, i, (j[0], j[1], 1, 1)) # 远程波形
pygame.display.flip()
pygame.time.delay(10)
if __name__ == "__main__":
print("START")
img()
print("LOADED")
grap()

BIN
testfield/visual/view.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@ -0,0 +1,17 @@
# 机体: Fuselage -> fuse
# 副翼: Aileron -> aile
# 襟翼: Flap -> flap
# 发动机: Engine -> eng
# 控制器: Controller -> ctrl
# 方向舵: Rudder -> rud
# 俯仰舵: Elevator -> elev
marking:
color_config:
fuselage: (255,255,255)
aileron: ()
flap:
engine:
controller: (255,255,255)
rudder:
elevator: