1
zhang
2025-09-10 b1e74bb24e7785176e59699cfe8eb4f217c958c8
1
3个文件已修改
1 文件已重命名
46个文件已添加
34437 ■■■■■ 已修改文件
.gitignore 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
LICENSE 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
README.md 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/ctu_agv_status.json 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/ctu_agv_status2.json 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/ctu_task_data.json 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/ctu_task_data2.json 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/environment.json 14960 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/path_mapping.json 11880 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/pom.xml 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/AlgorithmMain.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/SpringBootAplication.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/expose/AlgoSupport.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/expose/impl/AlgoSupportImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/AGVStatus.java 379 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/BackpackData.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/CTUPhysicalConfig.java 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/Conflict.java 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/ExecutingTask.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/PathCode.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/PlannedPath.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/TaskAssignment.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/model/TaskData.java 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/AStarPathPlanner.java 932 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/CollisionDetector.java 567 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/CollisionResolver.java 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/ExecutingTaskExtractor.java 478 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/PathPlanner.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/PathPlanningService.java 828 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/RemainingPathProcessor.java 416 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/service/TaskAllocationService.java 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/util/AgvTaskUtils.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/java/com/algo/util/JsonUtils.java 486 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/resources/META-INF/spring.factories 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
algo-zkd/src/main/resources/application.yml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/pom.xml 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/src/main/java/com/algo/entity/User.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/src/main/java/com/algo/service/UserService.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rpc-server/pom.xml 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rpc-server/src/main/java/com/algo/SpringBootRpcServerApplication.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rpc-server/src/main/resources/application.yml 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rpc-server/target/classes/application.yml 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/pom.xml 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/src/main/java/com/algo/mapper/UserMapper.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/src/main/java/com/algo/support/UserServiceSupport.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
service/src/main/resources/mapper/system/UserMapper.xml 补丁 | 查看 | 原始文档 | blame | 历史
web-server/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web-server/src/main/java/com/algo/SpringBootAplication.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web-server/src/main/java/com/algo/controller/UserController.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
web-server/src/main/resources/application.yml 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -1,4 +1,11 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
@@ -6,9 +13,14 @@
# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
target
.idea
.gitignore
.gitignore
LICENSE
New file
@@ -0,0 +1,201 @@
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/
   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
   1. Definitions.
      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.
      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.
      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.
      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.
      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.
      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.
      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).
      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.
      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."
      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.
   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.
   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.
   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:
      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and
      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and
      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and
      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.
      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.
   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.
   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.
   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.
   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.
   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.
   END OF TERMS AND CONDITIONS
   APPENDIX: How to apply the Apache License to your work.
      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.
   Copyright [yyyy] [name of copyright owner]
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
       http://www.apache.org/licenses/LICENSE-2.0
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
README.md
@@ -1,4 +1,19 @@
## algo-java
## 介绍
本项目技术站:
jdk8 + springboot + dubbo + mysql + redis + zookeeper
api模块提供最基础的实体类,枚举类等,最重要是需要暴露出去的抽象接口。
service写具体的接口的实现类,提供具体的实现类。
rpc-server里面是一个启动类,因为依赖service模块,可以理解为生产提供者。
web-server则是主要提供http接口给第三方,如前端调用,也可以引入其他项目的api模块,之后只要有生产者注入到注册中心就可以提供服务了,直接调用api里面的抽象接口方法。
中科大的老师,负责再service模块里写算法即可
1.service已经集成了redis和mysql也都有示例
2.api模块里只要写接口,供第三方系统使用
3.
algo-zkd/ctu_agv_status.json
New file
@@ -0,0 +1,48 @@
[
  {
    "agvId": "105",
    "status": 0,
    "position": "213",
    "empty": "2",
    "direction": "90",
    "vol": 85,
    "error": 0,
    "autoCharge": 30,
    "lowVol": 15,
    "currentPathIndex": 1,
    "nextPointArrivalTime": 1692345600000,
    "backpack": [
      {
        "index": 0,
        "loaded": false,
        "taskId": "TASK_001"
      },
      {
        "index": 1,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 2,
        "loaded": false,
        "taskId": null
      }
    ],
    "physicalConfig": {
      "maxSpeed": 2.0,
      "normalSpeed": 1.5,
      "maxAcceleration": 1.0,
      "maxDeceleration": 1.5,
      "turnTime90": 2.0,
      "turnTime180": 4.0,
      "minSafetyDistance": 3.0,
      "minFollowingDistance": 2.0,
      "ctuLength": 1.5,
      "ctuWidth": 1.0,
      "startupTime": 1.0,
      "stopTime": 1.5,
      "standardPointDistance": 1.0
    }
  }
]
algo-zkd/ctu_agv_status2.json
New file
@@ -0,0 +1,259 @@
[
  {
    "agvId": "CTU_001",
    "status": 0,
    "position": "1547",
    "empty": "2",
    "direction": "90",
    "vol": 85,
    "error": 0,
    "autoCharge": 30,
    "lowVol": 15,
    "currentPathIndex": 1,
    "nextPointArrivalTime": 1692345600000,
    "backpack": [
      {
        "index": 0,
        "loaded": false,
        "taskId": "TASK_001"
      },
      {
        "index": 1,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 2,
        "loaded": false,
        "taskId": null
      }
    ],
    "physicalConfig": {
      "maxSpeed": 2.0,
      "normalSpeed": 1.5,
      "maxAcceleration": 1.0,
      "maxDeceleration": 1.5,
      "turnTime90": 2.0,
      "turnTime180": 4.0,
      "minSafetyDistance": 3.0,
      "minFollowingDistance": 2.0,
      "ctuLength": 1.5,
      "ctuWidth": 1.0,
      "startupTime": 1.0,
      "stopTime": 1.5,
      "standardPointDistance": 1.0
    }
  },
  {
    "agvId": "CTU_002",
    "status": 0,
    "position": "592",
    "empty": "3",
    "direction": "270",
    "vol": 75,
    "error": 0,
    "autoCharge": 30,
    "lowVol": 15,
    "currentPathIndex": 0,
    "nextPointArrivalTime": 1692345620000,
    "backpack": [
      {
        "index": 0,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 1,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 2,
        "loaded": false,
        "taskId": null
      }
    ],
    "remainingPath": {
      "agvId": "CTU_002",
      "segId": "SEG_002_CTU_002_pickup",
      "codeList": [
        {
          "code": "592",
          "direction": "270",
          "actionType": "2",
          "taskId": "TASK_002",
          "posType": null,
          "lev": 0,
          "isTargetPoint": false
        },
        {
          "code": "599",
          "direction": "0",
          "actionType": "2",
          "taskId": "TASK_002",
          "posType": null,
          "lev": 0,
          "isTargetPoint": false
        },
        {
          "code": "647",
          "direction": "0",
          "actionType": "2",
          "taskId": "TASK_002",
          "posType": "1",
          "lev": 0,
          "isTargetPoint": true
        }
      ]
    },
    "physicalConfig": {
      "maxSpeed": 2.0,
      "normalSpeed": 1.5,
      "maxAcceleration": 1.0,
      "maxDeceleration": 1.5,
      "turnTime90": 2.0,
      "turnTime180": 4.0,
      "minSafetyDistance": 3.0,
      "minFollowingDistance": 2.0,
      "ctuLength": 1.5,
      "ctuWidth": 1.0,
      "startupTime": 1.0,
      "stopTime": 1.5,
      "standardPointDistance": 1.0
    }
  },
  {
    "agvId": "CTU_003",
    "status": 0,
    "position": "733",
    "empty": "3",
    "direction": "0",
    "vol": 90,
    "error": 0,
    "autoCharge": 30,
    "lowVol": 15,
    "currentPathIndex": 0,
    "nextPointArrivalTime": 1692345640000,
    "backpack": [
      {
        "index": 0,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 1,
        "loaded": false,
        "taskId": null
      },
      {
        "index": 2,
        "loaded": false,
        "taskId": null
      }
    ],
    "remainingPath": null,
    "physicalConfig": {
      "maxSpeed": 2.0,
      "normalSpeed": 1.5,
      "maxAcceleration": 1.0,
      "maxDeceleration": 1.5,
      "turnTime90": 2.0,
      "turnTime180": 4.0,
      "minSafetyDistance": 3.0,
      "minFollowingDistance": 2.0,
      "ctuLength": 1.5,
      "ctuWidth": 1.0,
      "startupTime": 1.0,
      "stopTime": 1.5,
      "standardPointDistance": 1.0
    }
  },
  {
    "agvId": "CTU_004",
    "status": 0,
    "position": "686",
    "empty": "2",
    "direction": "180",
    "vol": 25,
    "error": 0,
    "autoCharge": 30,
    "lowVol": 15,
    "currentPathIndex": 2,
    "nextPointArrivalTime": 1692345660000,
    "backpack": [
      {
        "index": 0,
        "loaded": true,
        "taskId": "TASK_004"
      },
      {
        "index": 1,
        "loaded": true,
        "taskId": "TASK_005"
      },
      {
        "index": 2,
        "loaded": false,
        "taskId": null
      }
    ],
    "remainingPath": {
      "agvId": "CTU_004",
      "segId": "SEG_004_CTU_004_charging",
      "codeList": [
        {
          "code": "686",
          "direction": "180",
          "actionType": "3",
          "taskId": null,
          "posType": null,
          "lev": 0,
          "isTargetPoint": false
        },
        {
          "code": "679",
          "direction": "270",
          "actionType": "3",
          "taskId": null,
          "posType": null,
          "lev": 0,
          "isTargetPoint": false
        },
        {
          "code": "654",
          "direction": "270",
          "actionType": "3",
          "taskId": null,
          "posType": null,
          "lev": 0,
          "isTargetPoint": false
        },
        {
          "code": "647",
          "direction": "270",
          "actionType": "3",
          "taskId": null,
          "posType": null,
          "lev": 0,
          "isTargetPoint": true
        }
      ]
    },
    "physicalConfig": {
      "maxSpeed": 2.0,
      "normalSpeed": 1.5,
      "maxAcceleration": 1.0,
      "maxDeceleration": 1.5,
      "turnTime90": 2.0,
      "turnTime180": 4.0,
      "minSafetyDistance": 3.0,
      "minFollowingDistance": 2.0,
      "ctuLength": 1.5,
      "ctuWidth": 1.0,
      "startupTime": 1.0,
      "stopTime": 1.5,
      "standardPointDistance": 1.0
    }
  }
]
algo-zkd/ctu_task_data.json
New file
@@ -0,0 +1,11 @@
[
  {
    "taskId": "TASK_001",
    "agvId": "105",
    "start": "238",
    "end": "250",
    "type": "1",
    "priority": 5,
    "taskTime": "2023-08-18 10:00:00"
  }
]
algo-zkd/ctu_task_data2.json
New file
@@ -0,0 +1,92 @@
[
  {
    "taskId": "TASK_001",
    "agvId": null,
    "start": "346",
    "end": "448",
    "type": "2",
    "priority": 5,
    "taskTime": "2023-08-18 10:00:00"
  },
  {
    "taskId": "TASK_002",
    "agvId": null,
    "start": "592",
    "end": "647",
    "type": "1",
    "priority": 3,
    "taskTime": "2023-08-18 10:05:00"
  },
  {
    "taskId": "TASK_003",
    "agvId": null,
    "start": "733",
    "end": "456",
    "type": "3",
    "priority": 4,
    "taskTime": "2023-08-18 10:10:00"
  },
  {
    "taskId": "TASK_004",
    "agvId": null,
    "start": "686",
    "end": "482",
    "type": "2",
    "priority": 6,
    "taskTime": "2023-08-18 10:15:00"
  },
  {
    "taskId": "TASK_005",
    "agvId": null,
    "start": "679",
    "end": "537",
    "type": "1",
    "priority": 2,
    "taskTime": "2023-08-18 10:20:00"
  },
  {
    "taskId": "CHARGE_001",
    "agvId": "CTU_004",
    "start": "686",
    "end": "647",
    "type": "5",
    "priority": 8,
    "taskTime": "2023-08-18 10:25:00"
  },
  {
    "taskId": "STANDBY_001",
    "agvId": "CTU_003",
    "start": "733",
    "end": "599",
    "type": "4",
    "priority": 1,
    "taskTime": "2023-08-18 10:30:00"
  },
  {
    "taskId": "TASK_006",
    "agvId": null,
    "start": "544",
    "end": "654",
    "type": "3",
    "priority": 7,
    "taskTime": "2023-08-18 10:35:00"
  },
  {
    "taskId": "TASK_007",
    "agvId": null,
    "start": "490",
    "end": "599",
    "type": "1",
    "priority": 4,
    "taskTime": "2023-08-18 10:40:00"
  },
  {
    "taskId": "TASK_008",
    "agvId": null,
    "start": "456",
    "end": "733",
    "type": "2",
    "priority": 3,
    "taskTime": "2023-08-18 10:45:00"
  }
]
algo-zkd/environment.json
New file
Diff too large
algo-zkd/path_mapping.json
New file
Diff too large
algo-zkd/pom.xml
New file
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zy</groupId>
    <artifactId>algo-zkd</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>
</project>
algo-zkd/src/main/java/com/algo/AlgorithmMain.java
New file
@@ -0,0 +1,67 @@
package com.algo;
import com.algo.model.AGVStatus;
import com.algo.model.TaskAssignment;
import com.algo.model.TaskData;
import com.algo.service.PathPlanningService;
import com.algo.service.TaskAllocationService;
import com.algo.util.AgvTaskUtils;
import com.algo.util.JsonUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import java.util.Map;
@SpringBootTest
@RunWith(SpringRunner.class)
public class AlgorithmMain {
    /**
     * 任务分配测试
     */
    @Test
    public void taskAllocationService() {
        Map<String, Map<String, Integer>> pathMapping = JsonUtils.loadPathMapping("path_mapping.json");
        System.out.println(pathMapping);
        Map<String, Object> environment = JsonUtils.loadEnvironment("environment.json");
        System.out.println(environment);
        TaskAllocationService taskAllocationService = new TaskAllocationService(pathMapping, environment);
        List<AGVStatus> agvStatusList = AgvTaskUtils.loadAgvStatus("ctu_agv_status.json");
        System.out.println(agvStatusList);
        List<TaskData> taskList = AgvTaskUtils.loadTaskList("ctu_task_data.json");
        System.out.println(taskList);
        System.out.println("执行任务分配方法:");
        List<TaskAssignment> taskAssignments = taskAllocationService.allocateTasks(agvStatusList, taskList);
        System.out.println(taskAssignments);
        System.out.println("验证任务分配结果:");
        boolean validateAssignments = taskAllocationService.validateAssignments(taskAssignments);
        System.out.println(validateAssignments);
    }
    /**
     * 路径优化测试
     */
    @Test
    public void pathPlanningService() {
        Map<String, Map<String, Integer>> pathMapping = JsonUtils.loadPathMapping("path_mapping.json");
        System.out.println(pathMapping);
        Map<String, Object> environment = JsonUtils.loadEnvironment("environment.json");
        System.out.println(environment);
        List<TaskData> taskList = AgvTaskUtils.loadTaskList("ctu_task_data.json");
        System.out.println(taskList);
        PathPlanningService pathPlanningService = new PathPlanningService(pathMapping, environment, taskList);
        List<AGVStatus> agvStatusList = AgvTaskUtils.loadAgvStatus("ctu_agv_status.json");
        System.out.println(agvStatusList);
        PathPlanningService.PathPlanningResult planningResult = pathPlanningService.planAllAgvPaths(agvStatusList, true, null);
        System.out.println(planningResult);
    }
}
algo-zkd/src/main/java/com/algo/SpringBootAplication.java
New file
@@ -0,0 +1,15 @@
package com.algo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootAplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAplication.class, args);
    }
}
algo-zkd/src/main/java/com/algo/expose/AlgoSupport.java
New file
@@ -0,0 +1,25 @@
package com.algo.expose;
import com.algo.model.AGVStatus;
import com.algo.model.TaskAssignment;
import com.algo.model.TaskData;
import com.algo.service.PathPlanningService;
import java.util.List;
public interface AlgoSupport {
    /**
     * 分配任务算法
     *
     * @return
     */
    List<TaskAssignment> allocateTasks(List<AGVStatus> agvStatusList, List<TaskData> taskList);
    /**
     * 找路径算法
     *
     * @return
     */
    PathPlanningService.PathPlanningResult planAllAgvPaths(List<AGVStatus> agvStatusList, boolean flag, List<double[]> constraints);
}
algo-zkd/src/main/java/com/algo/expose/impl/AlgoSupportImpl.java
New file
@@ -0,0 +1,26 @@
package com.algo.expose.impl;
import com.algo.expose.AlgoSupport;
import com.algo.model.AGVStatus;
import com.algo.model.TaskAssignment;
import com.algo.model.TaskData;
import com.algo.service.PathPlanningService;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
@Service
public class AlgoSupportImpl implements AlgoSupport {
    @Override
    public List<TaskAssignment> allocateTasks(List<AGVStatus> agvStatusList, List<TaskData> taskList) {
        return Collections.emptyList();
    }
    @Override
    public PathPlanningService.PathPlanningResult planAllAgvPaths(List<AGVStatus> agvStatusList, boolean flag, List<double[]> constraints) {
        return null;
    }
}
algo-zkd/src/main/java/com/algo/model/AGVStatus.java
New file
@@ -0,0 +1,379 @@
package com.algo.model;
import java.util.List;
import java.util.Objects;
/**
 * AGV状态信息
 * 包含AGV的完整状态数据,包括剩余路径和CTU物理参数
 */
public class AGVStatus {
    /**
     * AGV编号
     */
    private String agvId;
    /**
     * AGV状态
     */
    private int status;
    /**
     * AGV当前位置
     */
    private String position;
    /**
     * 空背篓数量
     */
    private String empty;
    /**
     * AGV方向角度
     */
    private String direction;
    /**
     * 电压电量值
     */
    private int vol;
    /**
     * 异常码,0表示正常
     */
    private int error;
    /**
     * 背篓数据列表
     */
    private List<BackpackData> backpack;
    /**
     * 低电量设定阈值,低于该值可以去自动充电也可以继续做任务
     */
    private int autoCharge;
    /**
     * 最低电量,电量低于该值必须去充电
     */
    private int lowVol;
    /**
     * CTU剩余未完成的路径,格式与输出路径相同
     */
    private PlannedPath remainingPath;
    /**
     * CTU物理参数配置
     */
    private CTUPhysicalConfig physicalConfig;
    /**
     * 当前执行路径中的位置索引(从剩余路径开始计算)
     */
    private int currentPathIndex;
    /**
     * 预计到达下一个路径点的时间戳(毫秒)
     */
    private long nextPointArrivalTime;
    // 构造函数
    public AGVStatus() {
        this.physicalConfig = new CTUPhysicalConfig(); // 使用默认配置
        this.currentPathIndex = 0;
        this.nextPointArrivalTime = System.currentTimeMillis();
    }
    public AGVStatus(String agvId, int status, String position, String empty,
                     String direction, int vol, int error, List<BackpackData> backpack,
                     int autoCharge, int lowVol) {
        this.agvId = agvId;
        this.status = status;
        this.position = position;
        this.empty = empty;
        this.direction = direction;
        this.vol = vol;
        this.error = error;
        this.backpack = backpack;
        this.autoCharge = autoCharge;
        this.lowVol = lowVol;
        this.physicalConfig = new CTUPhysicalConfig(); // 使用默认配置
        this.currentPathIndex = 0;
        this.nextPointArrivalTime = System.currentTimeMillis();
    }
    // 原有的Getter和Setter方法
    public String getAgvId() {
        return agvId;
    }
    public void setAgvId(String agvId) {
        this.agvId = agvId;
    }
    public int getStatus() {
        return status;
    }
    public void setStatus(int status) {
        this.status = status;
    }
    public String getPosition() {
        return position;
    }
    public void setPosition(String position) {
        this.position = position;
    }
    public String getEmpty() {
        return empty;
    }
    public void setEmpty(String empty) {
        this.empty = empty;
    }
    public String getDirection() {
        return direction;
    }
    public void setDirection(String direction) {
        this.direction = direction;
    }
    public int getVol() {
        return vol;
    }
    public void setVol(int vol) {
        this.vol = vol;
    }
    public int getError() {
        return error;
    }
    public void setError(int error) {
        this.error = error;
    }
    public List<BackpackData> getBackpack() {
        return backpack;
    }
    public void setBackpack(List<BackpackData> backpack) {
        this.backpack = backpack;
    }
    public int getAutoCharge() {
        return autoCharge;
    }
    public void setAutoCharge(int autoCharge) {
        this.autoCharge = autoCharge;
    }
    public int getLowVol() {
        return lowVol;
    }
    public void setLowVol(int lowVol) {
        this.lowVol = lowVol;
    }
    // 新增的剩余路径和物理参数相关的Getter和Setter方法
    public PlannedPath getRemainingPath() {
        return remainingPath;
    }
    public void setRemainingPath(PlannedPath remainingPath) {
        this.remainingPath = remainingPath;
    }
    public CTUPhysicalConfig getPhysicalConfig() {
        return physicalConfig;
    }
    public void setPhysicalConfig(CTUPhysicalConfig physicalConfig) {
        this.physicalConfig = physicalConfig;
    }
    public int getCurrentPathIndex() {
        return currentPathIndex;
    }
    public void setCurrentPathIndex(int currentPathIndex) {
        this.currentPathIndex = currentPathIndex;
    }
    public long getNextPointArrivalTime() {
        return nextPointArrivalTime;
    }
    public void setNextPointArrivalTime(long nextPointArrivalTime) {
        this.nextPointArrivalTime = nextPointArrivalTime;
    }
    /**
     * 检查CTU是否有剩余未完成的路径
     *
     * @return true如果有剩余路径
     */
    public boolean hasRemainingPath() {
        return remainingPath != null &&
                remainingPath.getCodeList() != null &&
                !remainingPath.getCodeList().isEmpty() &&
                currentPathIndex < remainingPath.getCodeList().size();
    }
    /**
     * 获取剩余路径的长度(从当前位置开始)
     *
     * @return 剩余路径点数量
     */
    public int getRemainingPathLength() {
        if (!hasRemainingPath()) {
            return 0;
        }
        return remainingPath.getCodeList().size() - currentPathIndex;
    }
    /**
     * 获取当前正在执行的路径点
     *
     * @return 当前路径点,如果没有则返回null
     */
    public PathCode getCurrentPathCode() {
        if (!hasRemainingPath()) {
            return null;
        }
        return remainingPath.getCodeList().get(currentPathIndex);
    }
    /**
     * 获取下一个要到达的路径点
     *
     * @return 下一个路径点,如果没有则返回null
     */
    public PathCode getNextPathCode() {
        if (!hasRemainingPath() || currentPathIndex + 1 >= remainingPath.getCodeList().size()) {
            return null;
        }
        return remainingPath.getCodeList().get(currentPathIndex + 1);
    }
    /**
     * 检查AGV是否可用(空闲且非故障状态)
     *
     * @return true如果AGV可用
     */
    public boolean isAvailable() {
        return error == 0 && status == 0 && vol > lowVol;
    }
    /**
     * 检查CTU是否可以接受新任务(考虑剩余路径)
     *
     * @return true如果可以接受新任务
     */
    public boolean canAcceptNewTask() {
        // 如果有剩余路径,需要等待完成后才能接受新任务
        // 除非剩余路径很短(比如只有1个点)
        if (hasRemainingPath()) {
            int remainingLength = getRemainingPathLength();
            return remainingLength <= 1; // 允许在即将完成当前路径时接受新任务
        }
        return isAvailable();
    }
    /**
     * 获取可用的背篓数量
     *
     * @return 可用背篓数量
     */
    public int getAvailableBackpackCount() {
        if (backpack == null || backpack.isEmpty()) {
            return 0;
        }
        int count = 0;
        for (BackpackData bp : backpack) {
            if (!bp.isLoaded() && bp.getTaskId() == null) {
                count++;
            }
        }
        return count;
    }
    /**
     * 查找可用的背篓位置
     *
     * @return 可用背篓的索引,如果没有可用则返回-1
     */
    public int findAvailableBackpackIndex() {
        if (backpack == null || backpack.isEmpty()) {
            return -1;
        }
        for (BackpackData bp : backpack) {
            if (!bp.isLoaded() && bp.getTaskId() == null) {
                return bp.getIndex();
            }
        }
        return -1;
    }
    /**
     * 检查AGV是否需要充电
     *
     * @return true如果需要充电
     */
    public boolean needsCharging() {
        return vol <= autoCharge;
    }
    /**
     * 检查AGV是否必须充电
     *
     * @return true如果必须充电
     */
    public boolean mustCharge() {
        return vol <= lowVol;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        AGVStatus agvStatus = (AGVStatus) o;
        return Objects.equals(agvId, agvStatus.agvId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(agvId);
    }
    @Override
    public String toString() {
        return "AGVStatus{" +
                "agvId='" + agvId + '\'' +
                ", status=" + status +
                ", position='" + position + '\'' +
                ", empty='" + empty + '\'' +
                ", direction='" + direction + '\'' +
                ", vol=" + vol +
                ", error=" + error +
                ", backpack=" + (backpack != null ? backpack.size() : 0) +
                ", autoCharge=" + autoCharge +
                ", lowVol=" + lowVol +
                ", hasRemainingPath=" + hasRemainingPath() +
                ", remainingPathLength=" + getRemainingPathLength() +
                ", physicalConfig=" + physicalConfig +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/BackpackData.java
New file
@@ -0,0 +1,123 @@
package com.algo.model;
import java.util.Objects;
/**
 * 背篓数据模型
 * 包含背篓的状态信息
 */
public class BackpackData {
    /**
     * 背篓编号
     */
    private int index;
    /**
     * 是否载货
     */
    private boolean loaded;
    /**
     * 对应的任务编号
     */
    private String taskId;
    private boolean execute;
    // 构造函数
    public BackpackData() {
    }
    public BackpackData(int index, boolean loaded, String taskId) {
        this.index = index;
        this.loaded = loaded;
        this.taskId = taskId;
    }
    // Getter和Setter方法
    public int getIndex() {
        return index;
    }
    public void setIndex(int index) {
        this.index = index;
    }
    public boolean isLoaded() {
        return loaded;
    }
    public void setLoaded(boolean loaded) {
        this.loaded = loaded;
    }
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public boolean isExecute() {
        return execute;
    }
    public void setExecute(boolean execute) {
        this.execute = execute;
    }
    /**
     * 检查背篓是否可用(未载货且无任务)
     *
     * @return true如果背篓可用
     */
    public boolean isAvailable() {
        return !loaded && (taskId == null || taskId.trim().isEmpty());
    }
    /**
     * 分配任务到背篓
     *
     * @param taskId 任务ID
     * @return true如果分配成功
     */
    public boolean assignTask(String taskId) {
        if (isAvailable()) {
            this.taskId = taskId;
            return true;
        }
        return false;
    }
    /**
     * 清空背篓任务
     */
    public void clearTask() {
        this.taskId = null;
        this.loaded = false;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        BackpackData that = (BackpackData) o;
        return index == that.index;
    }
    @Override
    public int hashCode() {
        return Objects.hash(index);
    }
    @Override
    public String toString() {
        return "BackpackData{" +
                "index=" + index +
                ", loaded=" + loaded +
                ", taskId='" + taskId + '\'' +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/CTUPhysicalConfig.java
New file
@@ -0,0 +1,322 @@
package com.algo.model;
/**
 * CTU物理参数配置类
 * 包含CTU运动的各种物理参数,用于路径规划和碰撞检测
 */
public class CTUPhysicalConfig {
    /**
     * 最大移动速度 (米/秒)
     */
    private double maxSpeed;
    /**
     * 正常移动速度 (米/秒)
     */
    private double normalSpeed;
    /**
     * 最大加速度 (米/秒²)
     */
    private double maxAcceleration;
    /**
     * 最大减速度 (米/秒²)
     */
    private double maxDeceleration;
    /**
     * 转向时间 (秒) - 90度转向所需时间
     */
    private double turnTime90;
    /**
     * 转向时间 (秒) - 180度转向所需时间
     */
    private double turnTime180;
    /**
     * CTU之间的最小安全距离 (米)
     */
    private double minSafetyDistance;
    /**
     * CTU之间的最小跟随距离 (米)
     */
    private double minFollowingDistance;
    /**
     * CTU的物理长度 (米)
     */
    private double ctuLength;
    /**
     * CTU的物理宽度 (米)
     */
    private double ctuWidth;
    /**
     * 启动时间 (秒) - 从静止到正常速度所需时间
     */
    private double startupTime;
    /**
     * 停止时间 (秒) - 从正常速度到静止所需时间
     */
    private double stopTime;
    /**
     * 路径点之间的标准距离 (米)
     */
    private double standardPointDistance;
    // 默认构造函数 - 使用推荐的默认值
    public CTUPhysicalConfig() {
        this.maxSpeed = 2.0;              // 2米/秒
        this.normalSpeed = 1.5;           // 1.5米/秒
        this.maxAcceleration = 1.0;       // 1米/秒²
        this.maxDeceleration = 1.5;       // 1.5米/秒²
        this.turnTime90 = 2.0;            // 90度转向需要2秒
        this.turnTime180 = 4.0;           // 180度转向需要4秒
        this.minSafetyDistance = 3.0;     // 最小安全距离3米
        this.minFollowingDistance = 2.0;  // 最小跟随距离2米
        this.ctuLength = 1.5;             // CTU长度1.5米
        this.ctuWidth = 1.0;              // CTU宽度1米
        this.startupTime = 1.0;           // 启动时间1秒
        this.stopTime = 1.5;              // 停止时间1.5秒
        this.standardPointDistance = 1.0; // 标准路径点距离1米
    }
    // 完整参数的构造函数
    public CTUPhysicalConfig(double maxSpeed, double normalSpeed, double maxAcceleration,
                             double maxDeceleration, double turnTime90, double turnTime180,
                             double minSafetyDistance, double minFollowingDistance,
                             double ctuLength, double ctuWidth, double startupTime,
                             double stopTime, double standardPointDistance) {
        this.maxSpeed = maxSpeed;
        this.normalSpeed = normalSpeed;
        this.maxAcceleration = maxAcceleration;
        this.maxDeceleration = maxDeceleration;
        this.turnTime90 = turnTime90;
        this.turnTime180 = turnTime180;
        this.minSafetyDistance = minSafetyDistance;
        this.minFollowingDistance = minFollowingDistance;
        this.ctuLength = ctuLength;
        this.ctuWidth = ctuWidth;
        this.startupTime = startupTime;
        this.stopTime = stopTime;
        this.standardPointDistance = standardPointDistance;
    }
    // Getter和Setter方法
    public double getMaxSpeed() {
        return maxSpeed;
    }
    public void setMaxSpeed(double maxSpeed) {
        this.maxSpeed = maxSpeed;
    }
    public double getNormalSpeed() {
        return normalSpeed;
    }
    public void setNormalSpeed(double normalSpeed) {
        this.normalSpeed = normalSpeed;
    }
    public double getMaxAcceleration() {
        return maxAcceleration;
    }
    public void setMaxAcceleration(double maxAcceleration) {
        this.maxAcceleration = maxAcceleration;
    }
    public double getMaxDeceleration() {
        return maxDeceleration;
    }
    public void setMaxDeceleration(double maxDeceleration) {
        this.maxDeceleration = maxDeceleration;
    }
    public double getTurnTime90() {
        return turnTime90;
    }
    public void setTurnTime90(double turnTime90) {
        this.turnTime90 = turnTime90;
    }
    public double getTurnTime180() {
        return turnTime180;
    }
    public void setTurnTime180(double turnTime180) {
        this.turnTime180 = turnTime180;
    }
    public double getMinSafetyDistance() {
        return minSafetyDistance;
    }
    public void setMinSafetyDistance(double minSafetyDistance) {
        this.minSafetyDistance = minSafetyDistance;
    }
    public double getMinFollowingDistance() {
        return minFollowingDistance;
    }
    public void setMinFollowingDistance(double minFollowingDistance) {
        this.minFollowingDistance = minFollowingDistance;
    }
    public double getCtuLength() {
        return ctuLength;
    }
    public void setCtuLength(double ctuLength) {
        this.ctuLength = ctuLength;
    }
    public double getCtuWidth() {
        return ctuWidth;
    }
    public void setCtuWidth(double ctuWidth) {
        this.ctuWidth = ctuWidth;
    }
    public double getStartupTime() {
        return startupTime;
    }
    public void setStartupTime(double startupTime) {
        this.startupTime = startupTime;
    }
    public double getStopTime() {
        return stopTime;
    }
    public void setStopTime(double stopTime) {
        this.stopTime = stopTime;
    }
    public double getStandardPointDistance() {
        return standardPointDistance;
    }
    public void setStandardPointDistance(double standardPointDistance) {
        this.standardPointDistance = standardPointDistance;
    }
    /**
     * 计算从当前速度加速到目标速度所需的时间
     *
     * @param currentSpeed 当前速度
     * @param targetSpeed  目标速度
     * @return 所需时间(秒)
     */
    public double getAccelerationTime(double currentSpeed, double targetSpeed) {
        if (targetSpeed > currentSpeed) {
            return (targetSpeed - currentSpeed) / maxAcceleration;
        } else {
            return (currentSpeed - targetSpeed) / maxDeceleration;
        }
    }
    /**
     * 计算从当前速度加速/减速到目标速度所需的距离
     *
     * @param currentSpeed 当前速度
     * @param targetSpeed  目标速度
     * @return 所需距离(米)
     */
    public double getAccelerationDistance(double currentSpeed, double targetSpeed) {
        double time = getAccelerationTime(currentSpeed, targetSpeed);
        return (currentSpeed + targetSpeed) * time / 2.0;
    }
    /**
     * 根据路径点距离计算移动时间
     *
     * @param distance     距离(米)
     * @param currentSpeed 当前速度
     * @return 移动时间(秒)
     */
    public double getTravelTime(double distance, double currentSpeed) {
        if (currentSpeed <= 0) {
            currentSpeed = normalSpeed;
        }
        return distance / currentSpeed;
    }
    /**
     * 根据方向变化计算转向时间
     *
     * @param fromDirection 起始方向角度
     * @param toDirection   目标方向角度
     * @return 转向时间(秒)
     */
    public double getTurnTime(String fromDirection, String toDirection) {
        try {
            double from = Double.parseDouble(fromDirection);
            double to = Double.parseDouble(toDirection);
            // 计算角度差
            double angleDiff = Math.abs(to - from);
            if (angleDiff > 180) {
                angleDiff = 360 - angleDiff;
            }
            // 根据角度差计算转向时间
            if (angleDiff <= 5) { // 基本无转向
                return 0.0;
            } else if (angleDiff <= 90) {
                return turnTime90 * (angleDiff / 90.0);
            } else if (angleDiff <= 180) {
                return turnTime90 + (turnTime180 - turnTime90) * ((angleDiff - 90) / 90.0);
            } else {
                return turnTime180;
            }
        } catch (NumberFormatException e) {
            // 如果方向不是数字,返回默认转向时间
            return turnTime90;
        }
    }
    /**
     * 检查两个CTU之间的距离是否满足安全要求
     *
     * @param distance    距离(米)
     * @param isFollowing 是否为跟随情况
     * @return true如果距离安全
     */
    public boolean isSafeDistance(double distance, boolean isFollowing) {
        double minDistance = isFollowing ? minFollowingDistance : minSafetyDistance;
        return distance >= minDistance;
    }
    @Override
    public String toString() {
        return "CTUPhysicalConfig{" +
                "maxSpeed=" + maxSpeed +
                ", normalSpeed=" + normalSpeed +
                ", maxAcceleration=" + maxAcceleration +
                ", maxDeceleration=" + maxDeceleration +
                ", turnTime90=" + turnTime90 +
                ", turnTime180=" + turnTime180 +
                ", minSafetyDistance=" + minSafetyDistance +
                ", minFollowingDistance=" + minFollowingDistance +
                ", ctuLength=" + ctuLength +
                ", ctuWidth=" + ctuWidth +
                ", startupTime=" + startupTime +
                ", stopTime=" + stopTime +
                ", standardPointDistance=" + standardPointDistance +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/Conflict.java
New file
@@ -0,0 +1,128 @@
package com.algo.model;
/**
 * 冲突模型
 * 表示AGV路径规划中的冲突信息
 */
public class Conflict {
    /**
     * 冲突类型:vertex(顶点冲突), edge(边冲突), follow(跟随冲突)
     */
    private String type;
    /**
     * 冲突AGV1
     */
    private String agv1;
    /**
     * 冲突AGV2
     */
    private String agv2;
    /**
     * 冲突发生的时间步
     */
    private int timeStep;
    /**
     * AGV1的冲突位置
     */
    private String position1;
    /**
     * AGV2的冲突位置
     */
    private String position2;
    /**
     * 冲突描述
     */
    private String description;
    // 构造函数
    public Conflict() {
    }
    public Conflict(String type, String agv1, String agv2, int timeStep,
                    String position1, String position2, String description) {
        this.type = type;
        this.agv1 = agv1;
        this.agv2 = agv2;
        this.timeStep = timeStep;
        this.position1 = position1;
        this.position2 = position2;
        this.description = description;
    }
    // Getter和Setter方法
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getAgv1() {
        return agv1;
    }
    public void setAgv1(String agv1) {
        this.agv1 = agv1;
    }
    public String getAgv2() {
        return agv2;
    }
    public void setAgv2(String agv2) {
        this.agv2 = agv2;
    }
    public int getTimeStep() {
        return timeStep;
    }
    public void setTimeStep(int timeStep) {
        this.timeStep = timeStep;
    }
    public String getPosition1() {
        return position1;
    }
    public void setPosition1(String position1) {
        this.position1 = position1;
    }
    public String getPosition2() {
        return position2;
    }
    public void setPosition2(String position2) {
        this.position2 = position2;
    }
    public String getDescription() {
        return description;
    }
    public void setDescription(String description) {
        this.description = description;
    }
    @Override
    public String toString() {
        return "Conflict{" +
                "type='" + type + '\'' +
                ", agv1='" + agv1 + '\'' +
                ", agv2='" + agv2 + '\'' +
                ", timeStep=" + timeStep +
                ", position1='" + position1 + '\'' +
                ", position2='" + position2 + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/ExecutingTask.java
New file
@@ -0,0 +1,172 @@
package com.algo.model;
/**
 * 执行中任务模型
 * 表示AGV当前正在执行的任务信息
 */
public class ExecutingTask {
    /**
     * AGV编号
     */
    private String agvId;
    /**
     * 任务ID
     */
    private String taskId;
    /**
     * 当前位置
     */
    private String currentPosition;
    /**
     * 目标位置
     */
    private String targetPosition;
    /**
     * 任务类型
     */
    private String taskType;
    /**
     * 是否已装载
     */
    private boolean loaded;
    /**
     * 背篓索引
     */
    private int backpackIndex;
    /**
     * 任务优先级
     */
    private int priority;
    /**
     * 距离起点的距离
     */
    private double distanceToStart;
    /**
     * 距离终点的距离
     */
    private double distanceToEnd;
    // 构造函数
    public ExecutingTask() {
    }
    public ExecutingTask(String agvId, String taskId, String currentPosition,
                         String targetPosition, String taskType, boolean loaded,
                         int backpackIndex, int priority) {
        this.agvId = agvId;
        this.taskId = taskId;
        this.currentPosition = currentPosition;
        this.targetPosition = targetPosition;
        this.taskType = taskType;
        this.loaded = loaded;
        this.backpackIndex = backpackIndex;
        this.priority = priority;
    }
    // Getter和Setter方法
    public String getAgvId() {
        return agvId;
    }
    public void setAgvId(String agvId) {
        this.agvId = agvId;
    }
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public String getCurrentPosition() {
        return currentPosition;
    }
    public void setCurrentPosition(String currentPosition) {
        this.currentPosition = currentPosition;
    }
    public String getTargetPosition() {
        return targetPosition;
    }
    public void setTargetPosition(String targetPosition) {
        this.targetPosition = targetPosition;
    }
    public String getTaskType() {
        return taskType;
    }
    public void setTaskType(String taskType) {
        this.taskType = taskType;
    }
    public boolean isLoaded() {
        return loaded;
    }
    public void setLoaded(boolean loaded) {
        this.loaded = loaded;
    }
    public int getBackpackIndex() {
        return backpackIndex;
    }
    public void setBackpackIndex(int backpackIndex) {
        this.backpackIndex = backpackIndex;
    }
    public int getPriority() {
        return priority;
    }
    public void setPriority(int priority) {
        this.priority = priority;
    }
    public double getDistanceToStart() {
        return distanceToStart;
    }
    public void setDistanceToStart(double distanceToStart) {
        this.distanceToStart = distanceToStart;
    }
    public double getDistanceToEnd() {
        return distanceToEnd;
    }
    public void setDistanceToEnd(double distanceToEnd) {
        this.distanceToEnd = distanceToEnd;
    }
    @Override
    public String toString() {
        return "ExecutingTask{" +
                "agvId='" + agvId + '\'' +
                ", taskId='" + taskId + '\'' +
                ", currentPosition='" + currentPosition + '\'' +
                ", targetPosition='" + targetPosition + '\'' +
                ", taskType='" + taskType + '\'' +
                ", loaded=" + loaded +
                ", backpackIndex=" + backpackIndex +
                ", priority=" + priority +
                ", distanceToStart=" + distanceToStart +
                ", distanceToEnd=" + distanceToEnd +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/PathCode.java
New file
@@ -0,0 +1,136 @@
package com.algo.model;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
 * 路径代码模型
 * 表示路径中的单个节点信息
 */
public class PathCode {
    /**
     * 路径点编号
     */
    private String code;
    /**
     * 方向角度
     */
    private String direction;
    /**
     * 动作类型
     */
    private String actionType;
    /**
     * 任务ID
     */
    private String taskId;
    /**
     * 位置类型
     */
    private String posType;
    /**
     * 背篓层级
     */
    private int lev;
    /**
     * 是否为目标点
     */
    @JsonProperty("isTargetPoint")
    private boolean isTargetPoint;
    // 构造函数
    public PathCode() {
    }
    public PathCode(String code, String direction) {
        this.code = code;
        this.direction = direction;
    }
    public PathCode(String code, String direction, String actionType, String taskId,
                    String posType, int lev, boolean isTargetPoint) {
        this.code = code;
        this.direction = direction;
        this.actionType = actionType;
        this.taskId = taskId;
        this.posType = posType;
        this.lev = lev;
        this.isTargetPoint = isTargetPoint;
    }
    // Getter和Setter方法
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getDirection() {
        return direction;
    }
    public void setDirection(String direction) {
        this.direction = direction;
    }
    public String getActionType() {
        return actionType;
    }
    public void setActionType(String actionType) {
        this.actionType = actionType;
    }
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public String getPosType() {
        return posType;
    }
    public void setPosType(String posType) {
        this.posType = posType;
    }
    public int getLev() {
        return lev;
    }
    public void setLev(int lev) {
        this.lev = lev;
    }
    public boolean isTargetPoint() {
        return isTargetPoint;
    }
    public void setTargetPoint(boolean targetPoint) {
        isTargetPoint = targetPoint;
    }
    @Override
    public String toString() {
        return "PathCode{" +
                "code='" + code + '\'' +
                ", direction='" + direction + '\'' +
                ", actionType='" + actionType + '\'' +
                ", taskId='" + taskId + '\'' +
                ", posType='" + posType + '\'' +
                ", lev=" + lev +
                ", isTargetPoint=" + isTargetPoint +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/PlannedPath.java
New file
@@ -0,0 +1,99 @@
package com.algo.model;
import java.util.List;
/**
 * 规划路径模型
 * 表示AGV的完整路径规划结果
 */
public class PlannedPath {
    /**
     * AGV编号
     */
    private String agvId;
    /**
     * 段落ID
     */
    private String segId;
    /**
     * 路径代码列表
     */
    private List<PathCode> codeList;
    // 构造函数
    public PlannedPath() {
    }
    public PlannedPath(String agvId, String segId, List<PathCode> codeList) {
        this.agvId = agvId;
        this.segId = segId;
        this.codeList = codeList;
    }
    // Getter和Setter方法
    public String getAgvId() {
        return agvId;
    }
    public void setAgvId(String agvId) {
        this.agvId = agvId;
    }
    public String getSegId() {
        return segId;
    }
    public void setSegId(String segId) {
        this.segId = segId;
    }
    public List<PathCode> getCodeList() {
        return codeList;
    }
    public void setCodeList(List<PathCode> codeList) {
        this.codeList = codeList;
    }
    /**
     * 获取路径长度
     *
     * @return 路径长度
     */
    public int getPathLength() {
        return codeList != null ? codeList.size() : 0;
    }
    /**
     * 获取起始位置
     *
     * @return 起始位置编号
     */
    public String getStartPosition() {
        return (codeList != null && !codeList.isEmpty()) ? codeList.get(0).getCode() : null;
    }
    /**
     * 获取终点位置
     *
     * @return 终点位置编号
     */
    public String getEndPosition() {
        return (codeList != null && !codeList.isEmpty()) ?
                codeList.get(codeList.size() - 1).getCode() : null;
    }
    @Override
    public String toString() {
        return "PlannedPath{" +
                "agvId='" + agvId + '\'' +
                ", segId='" + segId + '\'' +
                ", pathLength=" + getPathLength() +
                ", startPosition='" + getStartPosition() + '\'' +
                ", endPosition='" + getEndPosition() + '\'' +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/TaskAssignment.java
New file
@@ -0,0 +1,183 @@
package com.algo.model;
import java.util.Objects;
/**
 * 任务分配结果数据模型
 * 包含任务分配的完整结果信息
 */
public class TaskAssignment {
    /**
     * 任务ID
     */
    private String taskId;
    /**
     * 分配的AGV编号
     */
    private String agvId;
    /**
     * 片段ID,格式:任务编号_AGV编号_序号
     */
    private String segId;
    /**
     * 使用的背篓编号
     */
    private String lev;
    /**
     * 动作类型:take取货 put放货
     */
    private String type;
    // 动作类型常量
    public static final String TYPE_TAKE = "take";  // 取货
    public static final String TYPE_PUT = "put";    // 放货
    // 构造函数
    public TaskAssignment() {
    }
    public TaskAssignment(String taskId, String agvId, String segId, String lev, String type) {
        this.taskId = taskId;
        this.agvId = agvId;
        this.segId = segId;
        this.lev = lev;
        this.type = type;
    }
    // Getter和Setter方法
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public String getAgvId() {
        return agvId;
    }
    public void setAgvId(String agvId) {
        this.agvId = agvId;
    }
    public String getSegId() {
        return segId;
    }
    public void setSegId(String segId) {
        this.segId = segId;
    }
    public String getLev() {
        return lev;
    }
    public void setLev(String lev) {
        this.lev = lev;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    /**
     * 生成片段ID
     *
     * @param taskId   任务ID
     * @param agvId    AGV编号
     * @param sequence 序号(1表示取货,2表示放货)
     * @return 片段ID
     */
    public static String generateSegId(String taskId, String agvId, int sequence) {
        return taskId + "_" + agvId + "_" + sequence;
    }
    /**
     * 检查是否为取货任务
     *
     * @return true如果是取货任务
     */
    public boolean isTakeTask() {
        return TYPE_TAKE.equals(type);
    }
    /**
     * 检查是否为放货任务
     *
     * @return true如果是放货任务
     */
    public boolean isPutTask() {
        return TYPE_PUT.equals(type);
    }
    /**
     * 解析片段ID获取序号
     *
     * @return 序号,如果解析失败返回0
     */
    public int parseSequenceFromSegId() {
        if (segId == null || segId.trim().isEmpty()) {
            return 0;
        }
        try {
            String[] parts = segId.split("_");
            if (parts.length >= 3) {
                return Integer.parseInt(parts[2]);
            }
        } catch (NumberFormatException e) {
            // 忽略解析错误
        }
        return 0;
    }
    /**
     * 验证任务分配结果是否有效
     *
     * @return true如果有效
     */
    public boolean isValid() {
        return taskId != null && !taskId.trim().isEmpty() &&
                agvId != null && !agvId.trim().isEmpty() &&
                segId != null && !segId.trim().isEmpty() &&
                lev != null && !lev.trim().isEmpty() &&
                type != null && (TYPE_TAKE.equals(type) || TYPE_PUT.equals(type));
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TaskAssignment that = (TaskAssignment) o;
        return Objects.equals(taskId, that.taskId) &&
                Objects.equals(agvId, that.agvId) &&
                Objects.equals(segId, that.segId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(taskId, agvId, segId);
    }
    @Override
    public String toString() {
        return "TaskAssignment{" +
                "taskId='" + taskId + '\'' +
                ", agvId='" + agvId + '\'' +
                ", segId='" + segId + '\'' +
                ", lev='" + lev + '\'' +
                ", type='" + type + '\'' +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/model/TaskData.java
New file
@@ -0,0 +1,228 @@
package com.algo.model;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
/**
 * 任务数据模型
 * 包含任务的完整信息
 */
public class TaskData {
    /**
     * 任务ID
     */
    private String taskId;
    /**
     * AGV编号(仅充电任务和去待机位任务使用)
     */
    private String agvId;
    /**
     * 任务起点
     */
    private String start;
    /**
     * 任务终点
     */
    private String end;
    /**
     * 任务类型:1入库 2出库 3移库 4去待机位 5充电
     */
    private String type;
    /**
     * 任务优先级,越大优先级越高
     */
    private int priority;
    /**
     * 任务创建时间
     */
    private String taskTime;
    // 任务类型常量
    public static final String TYPE_INBOUND = "1";    // 入库
    public static final String TYPE_OUTBOUND = "2";   // 出库
    public static final String TYPE_MOVE = "3";       // 移库
    public static final String TYPE_STANDBY = "4";    // 去待机位
    public static final String TYPE_CHARGE = "5";     // 充电
    // 构造函数
    public TaskData() {
    }
    public TaskData(String taskId, String agvId, String start, String end,
                    String type, int priority, String taskTime) {
        this.taskId = taskId;
        this.agvId = agvId;
        this.start = start;
        this.end = end;
        this.type = type;
        this.priority = priority;
        this.taskTime = taskTime;
    }
    // Getter和Setter方法
    public String getTaskId() {
        return taskId;
    }
    public void setTaskId(String taskId) {
        this.taskId = taskId;
    }
    public String getAgvId() {
        return agvId;
    }
    public void setAgvId(String agvId) {
        this.agvId = agvId;
    }
    public String getStart() {
        return start;
    }
    public void setStart(String start) {
        this.start = start;
    }
    public String getEnd() {
        return end;
    }
    public void setEnd(String end) {
        this.end = end;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public int getPriority() {
        return priority;
    }
    public void setPriority(int priority) {
        this.priority = priority;
    }
    public String getTaskTime() {
        return taskTime;
    }
    public void setTaskTime(String taskTime) {
        this.taskTime = taskTime;
    }
    /**
     * 检查是否为运输任务(需要取货和送货)
     *
     * @return true如果是运输任务
     */
    public boolean isTransportTask() {
        return TYPE_INBOUND.equals(type) || TYPE_OUTBOUND.equals(type) || TYPE_MOVE.equals(type);
    }
    /**
     * 检查是否为充电任务
     *
     * @return true如果是充电任务
     */
    public boolean isChargeTask() {
        return TYPE_CHARGE.equals(type);
    }
    /**
     * 检查是否为去待机位任务
     *
     * @return true如果是去待机位任务
     */
    public boolean isStandbyTask() {
        return TYPE_STANDBY.equals(type);
    }
    /**
     * 检查是否为指定AGV的任务
     *
     * @return true如果是指定AGV的任务
     */
    public boolean isAssignedToAgv() {
        return agvId != null && !agvId.trim().isEmpty();
    }
    /**
     * 解析任务创建时间
     *
     * @return LocalDateTime对象
     */
    public LocalDateTime parseTaskTime() {
        if (taskTime == null || taskTime.trim().isEmpty()) {
            return null;
        }
        try {
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse(taskTime, formatter);
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 获取任务类型描述
     *
     * @return 任务类型描述
     */
    public String getTypeDescription() {
        switch (type) {
            case TYPE_INBOUND:
                return "入库";
            case TYPE_OUTBOUND:
                return "出库";
            case TYPE_MOVE:
                return "移库";
            case TYPE_STANDBY:
                return "去待机位";
            case TYPE_CHARGE:
                return "充电";
            default:
                return "未知任务类型";
        }
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TaskData taskData = (TaskData) o;
        return Objects.equals(taskId, taskData.taskId);
    }
    @Override
    public int hashCode() {
        return Objects.hash(taskId);
    }
    @Override
    public String toString() {
        return "TaskData{" +
                "taskId='" + taskId + '\'' +
                ", agvId='" + agvId + '\'' +
                ", start='" + start + '\'' +
                ", end='" + end + '\'' +
                ", type='" + type + '\'' +
                ", priority=" + priority +
                ", taskTime='" + taskTime + '\'' +
                '}';
    }
}
algo-zkd/src/main/java/com/algo/service/AStarPathPlanner.java
New file
@@ -0,0 +1,932 @@
package com.algo.service;
import com.algo.model.CTUPhysicalConfig;
import com.algo.model.PathCode;
import com.algo.model.PlannedPath;
import com.algo.util.JsonUtils;
import java.util.*;
/**
 * A*路径规划器实现
 * 使用3D A*算法进行CTU时空路径规划,支持物理约束
 */
public class AStarPathPlanner implements PathPlanner {
    /**
     * 路径映射表
     */
    private final Map<String, Map<String, Integer>> pathMapping;
    /**
     * 邻接表
     */
    private final Map<String, List<Map<String, String>>> adjacencyList;
    /**
     * 时间精度(毫秒)
     */
    private final long timeResolution = 1000;
    /**
     * 最大搜索时间(毫秒)
     */
    private final long maxSearchTime = 30000; // 30秒
    /**
     * 最大搜索深度
     */
    private final int maxSearchDepth = 15000;
    /**
     * 距离缓存 - 优化:缓存距离计算结果
     */
    private final Map<String, Double> distanceCache = new HashMap<>();
    /**
     * 快速路径缓存
     */
    private final Map<String, List<String>> fastPathCache = new HashMap<>();
    /**
     * 构造函数
     *
     * @param pathMapping 路径映射表
     */
    public AStarPathPlanner(Map<String, Map<String, Integer>> pathMapping) {
        this.pathMapping = pathMapping;
        this.adjacencyList = new HashMap<>();
        // 直接构建邻接表
        buildAdjacencyList();
        // 预计算常用距离
        precomputeCommonDistances();
        System.out.println("A*路径规划器初始化完成,邻接表包含 " + adjacencyList.size() + " 个节点");
    }
    @Override
    public PlannedPath planPath(String startCode, String endCode, List<double[]> constraints) {
        // 先尝试快速路径规划,失败后再使用完整的时空规划
        PlannedPath fastPath = tryFastPathPlanning(startCode, endCode, constraints);
        if (fastPath != null) {
            return fastPath;
        }
        return planSpaceTimePath(startCode, endCode, constraints, null, null);
    }
    @Override
    public PlannedPath planPath(String startCode, String endCode) {
        return planPath(startCode, endCode, null);
    }
    /**
     * 规划时空路径
     *
     * @param startCode             起始路径点ID
     * @param endCode               目标路径点ID
     * @param constraints           静态约束条件
     * @param spaceTimeOccupancyMap 时空占用表
     * @param physicalConfig        CTU物理配置
     * @return 规划的路径
     */
    public PlannedPath planSpaceTimePath(String startCode, String endCode,
                                         List<double[]> constraints,
                                         Map<String, String> spaceTimeOccupancyMap,
                                         CTUPhysicalConfig physicalConfig) {
        // 验证输入
        if (!isValidPathPoint(startCode) || !isValidPathPoint(endCode)) {
            System.out.println("无效的路径点: " + startCode + " 或 " + endCode);
            return null;
        }
        // 起点和终点相同
        if (startCode.equals(endCode)) {
            List<PathCode> codeList = new ArrayList<>();
            codeList.add(new PathCode(startCode, "90"));
            return new PlannedPath("", "", codeList);
        }
        // 使用默认物理配置
        if (physicalConfig == null) {
            physicalConfig = createDefaultPhysicalConfig();
        }
        // 获取起点和终点坐标
        int[] startCoord = JsonUtils.getCoordinate(startCode, pathMapping);
        int[] endCoord = JsonUtils.getCoordinate(endCode, pathMapping);
        if (startCoord == null || endCoord == null) {
            System.out.println("无法获取坐标: " + startCode + " 或 " + endCode);
            return null;
        }
        System.out.println("开始时空A*搜索: " + startCode + " -> " + endCode);
        // 时空A*算法实现
        PlannedPath result = spaceTimeAStarSearch(
                startCode, endCode, startCoord, endCoord,
                constraints, spaceTimeOccupancyMap, physicalConfig
        );
        if (result != null) {
            System.out.println("时空路径规划成功,路径长度: " + result.getCodeList().size());
        } else {
            System.out.println("时空路径规划失败");
        }
        return result;
    }
    /**
     * 时空A*搜索算法实现
     *
     * @param startCode      起始路径点ID
     * @param endCode        目标路径点ID
     * @param startCoord     起始坐标
     * @param endCoord       目标坐标
     * @param constraints    约束条件
     * @param occupancyMap   时空占用表
     * @param physicalConfig 物理配置
     * @return 规划的路径
     */
    private PlannedPath spaceTimeAStarSearch(String startCode, String endCode,
                                             int[] startCoord, int[] endCoord,
                                             List<double[]> constraints,
                                             Map<String, String> occupancyMap,
                                             CTUPhysicalConfig physicalConfig) {
        // 使用优先队列实现开放列表
        PriorityQueue<SpaceTimeAStarNode> openSet = new PriorityQueue<>(
                Comparator.comparingDouble(n -> n.fScore)
        );
        Set<String> closedSet = new HashSet<>();
        Map<String, Double> gScores = new HashMap<>();
        Map<String, SpaceTimeAStarNode> cameFrom = new HashMap<>();
        // 起始时间
        long startTime = System.currentTimeMillis();
        // 初始化起始节点
        SpaceTimeAStarNode startNode = new SpaceTimeAStarNode(
                startCode, startCoord, startTime, 0,
                spaceTimeHeuristic(startCoord, endCoord, startTime, physicalConfig)
        );
        openSet.offer(startNode);
        String startKey = createSpaceTimeKey(startCode, startTime);
        gScores.put(startKey, 0.0);
        // 构建约束检查器
        EnhancedConstraintChecker constraintChecker = new EnhancedConstraintChecker(
                constraints, occupancyMap, pathMapping, physicalConfig
        );
        int searchDepth = 0;
        long searchStartTime = System.currentTimeMillis();
        while (!openSet.isEmpty()) {
            // 检查搜索时间和深度限制
            if (System.currentTimeMillis() - searchStartTime > maxSearchTime ||
                    searchDepth > maxSearchDepth) {
                System.out.println("搜索超时或达到最大深度,返回null");
                break;
            }
            SpaceTimeAStarNode current = openSet.poll();
            String currentKey = createSpaceTimeKey(current.code, current.timePoint);
            if (closedSet.contains(currentKey)) {
                continue;
            }
            closedSet.add(currentKey);
            searchDepth++;
            // 到达目标
            if (current.code.equals(endCode)) {
                System.out.println("找到目标,重建路径,搜索深度: " + searchDepth);
                return reconstructSpaceTimePath(cameFrom, startNode, current, physicalConfig);
            }
            // 扩展邻居节点
            expandNeighbors(current, endCoord, openSet, closedSet, gScores, cameFrom,
                    constraintChecker, physicalConfig);
        }
        System.out.println("未找到有效路径,搜索深度: " + searchDepth);
        return null; // 无法找到路径
    }
    /**
     * 扩展邻居节点
     */
    private void expandNeighbors(SpaceTimeAStarNode current, int[] endCoord,
                                 PriorityQueue<SpaceTimeAStarNode> openSet,
                                 Set<String> closedSet, Map<String, Double> gScores,
                                 Map<String, SpaceTimeAStarNode> cameFrom,
                                 EnhancedConstraintChecker constraintChecker,
                                 CTUPhysicalConfig physicalConfig) {
        // 获取空间邻居
        List<Map<String, String>> neighbors = getNeighbors(current.code);
        for (Map<String, String> neighbor : neighbors) {
            String neighborCode = neighbor.get("code");
            String direction = neighbor.get("direction");
            // 计算移动到邻居的时间成本
            long travelTime = calculateTravelTime(current.code, neighborCode, direction, physicalConfig);
            long arrivalTime = current.timePoint + travelTime;
            // 时间对齐到分辨率
            arrivalTime = alignToTimeResolution(arrivalTime);
            String neighborKey = createSpaceTimeKey(neighborCode, arrivalTime);
            if (closedSet.contains(neighborKey)) {
                continue;
            }
            // 约束检查
            if (!constraintChecker.isValidMove(current.code, neighborCode,
                    current.timePoint, arrivalTime)) {
                continue;
            }
            // 计算G值
            double gScore = gScores.get(createSpaceTimeKey(current.code, current.timePoint)) +
                    travelTime / 1000.0; // 转换为秒
            // 更新最佳路径
            if (!gScores.containsKey(neighborKey) || gScore < gScores.get(neighborKey)) {
                gScores.put(neighborKey, gScore);
                // 获取邻居坐标
                int[] neighborCoord = JsonUtils.getCoordinate(neighborCode, pathMapping);
                if (neighborCoord != null) {
                    // 创建邻居节点
                    SpaceTimeAStarNode neighborNode = new SpaceTimeAStarNode(
                            neighborCode, neighborCoord, arrivalTime, gScore,
                            gScore + spaceTimeHeuristic(neighborCoord, endCoord, arrivalTime, physicalConfig)
                    );
                    cameFrom.put(neighborKey, current);
                    openSet.offer(neighborNode);
                }
            }
        }
        // 减少等待选项 - 只在必要时等待(20%概率)
        if (Math.random() < 0.2) {
            long waitTime = timeResolution;
            long waitUntilTime = current.timePoint + waitTime;
            String waitKey = createSpaceTimeKey(current.code, waitUntilTime);
            if (!closedSet.contains(waitKey) &&
                    constraintChecker.isValidWait(current.code, current.timePoint, waitUntilTime)) {
                double waitGScore = gScores.get(createSpaceTimeKey(current.code, current.timePoint)) +
                        (waitTime / 1000.0);
                if (!gScores.containsKey(waitKey) || waitGScore < gScores.get(waitKey)) {
                    gScores.put(waitKey, waitGScore);
                    SpaceTimeAStarNode waitNode = new SpaceTimeAStarNode(
                            current.code, current.coordinates, waitUntilTime, waitGScore,
                            waitGScore + spaceTimeHeuristic(current.coordinates, endCoord, waitUntilTime, physicalConfig)
                    );
                    cameFrom.put(waitKey, current);
                    openSet.offer(waitNode);
                }
            }
        }
    }
    /**
     * 时空启发式函数
     *
     * @param coord1         当前坐标
     * @param coord2         目标坐标
     * @param currentTime    当前时间
     * @param physicalConfig 物理配置
     * @return 启发式值
     */
    private double spaceTimeHeuristic(int[] coord1, int[] coord2, long currentTime, CTUPhysicalConfig physicalConfig) {
        // 空间距离
        double spatialDistance = Math.abs(coord1[0] - coord2[0]) + Math.abs(coord1[1] - coord2[1]);
        // 时间成本估计
        double timeEstimate = spatialDistance * physicalConfig.getStandardPointDistance() / physicalConfig.getNormalSpeed();
        // 考虑转向成本
        double turnPenalty = spatialDistance > 1 ? physicalConfig.getTurnTime90() : 0;
        return timeEstimate + turnPenalty;
    }
    /**
     * 计算移动时间
     *
     * @param fromCode       起始代码
     * @param toCode         目标代码
     * @param direction      移动方向
     * @param physicalConfig 物理配置
     * @return 移动时间(毫秒)
     */
    private long calculateTravelTime(String fromCode, String toCode, String direction, CTUPhysicalConfig physicalConfig) {
        // 基本移动时间
        double distance = physicalConfig.getStandardPointDistance();
        double speed = physicalConfig.getNormalSpeed();
        long moveTime = (long) ((distance / speed) * 1000);
        // 转向时间
        long turnTime = (long) (physicalConfig.getTurnTime90() * 1000 / 4); // 假设平均转向时间
        return moveTime + turnTime;
    }
    /**
     * 重建时空路径
     *
     * @param cameFrom       路径来源映射
     * @param startNode      起始节点
     * @param endNode        结束节点
     * @param physicalConfig 物理配置
     * @return 重建的路径
     */
    private PlannedPath reconstructSpaceTimePath(Map<String, SpaceTimeAStarNode> cameFrom,
                                                 SpaceTimeAStarNode startNode,
                                                 SpaceTimeAStarNode endNode,
                                                 CTUPhysicalConfig physicalConfig) {
        List<SpaceTimeAStarNode> pathNodes = new ArrayList<>();
        SpaceTimeAStarNode current = endNode;
        // 反向重建路径
        while (current != null) {
            pathNodes.add(0, current);
            String currentKey = createSpaceTimeKey(current.code, current.timePoint);
            current = cameFrom.get(currentKey);
        }
        // 转换为PathCode列表
        List<PathCode> codeList = new ArrayList<>();
        for (int i = 0; i < pathNodes.size(); i++) {
            SpaceTimeAStarNode node = pathNodes.get(i);
            String direction = "90"; // 默认方向
            // 计算方向
            if (i < pathNodes.size() - 1) {
                SpaceTimeAStarNode nextNode = pathNodes.get(i + 1);
                direction = calculateDirection(node.code, nextNode.code);
            }
            PathCode pathCode = new PathCode(node.code, direction);
            // 添加时间信息
            // pathCode.setArrivalTime(node.timePoint);
            codeList.add(pathCode);
        }
        return new PlannedPath("", "", codeList);
    }
    /**
     * 创建时空键
     *
     * @param code      位置代码
     * @param timePoint 时间点
     * @return 时空键
     */
    private String createSpaceTimeKey(String code, long timePoint) {
        return code + "_T" + timePoint;
    }
    /**
     * 时间对齐到分辨率
     *
     * @param timePoint 时间点
     * @return 对齐后的时间
     */
    private long alignToTimeResolution(long timePoint) {
        return (timePoint / timeResolution) * timeResolution;
    }
    /**
     * 创建默认物理配置
     *
     * @return 默认配置
     */
    private CTUPhysicalConfig createDefaultPhysicalConfig() {
        CTUPhysicalConfig config = new CTUPhysicalConfig();
        config.setMaxSpeed(2.0);
        config.setNormalSpeed(1.5);
        config.setMaxAcceleration(1.0);
        config.setMaxDeceleration(1.5);
        config.setTurnTime90(2.0);
        config.setTurnTime180(4.0);
        config.setMinSafetyDistance(3.0);
        config.setMinFollowingDistance(2.0);
        config.setCtuLength(1.2);
        config.setCtuWidth(0.8);
        config.setStartupTime(1.0);
        config.setStopTime(0.5);
        config.setStandardPointDistance(1.0);
        return config;
    }
    /**
     * 计算移动方向
     *
     * @param fromCode 起始路径点
     * @param toCode   目标路径点
     * @return 方向角度字符串
     */
    private String calculateDirection(String fromCode, String toCode) {
        int[] fromCoord = JsonUtils.getCoordinate(fromCode, pathMapping);
        int[] toCoord = JsonUtils.getCoordinate(toCode, pathMapping);
        if (fromCoord == null || toCoord == null) {
            return "90";
        }
        int dx = toCoord[0] - fromCoord[0];
        int dy = toCoord[1] - fromCoord[1];
        // 确定方向
        if (dx == 0 && dy == -1) return "270"; // 上
        if (dx == 1 && dy == 0) return "0";    // 右
        if (dx == 0 && dy == 1) return "90";   // 下
        if (dx == -1 && dy == 0) return "180"; // 左
        return "90"; // 默认方向
    }
    @Override
    public boolean isValidPathPoint(String pathCode) {
        return pathCode != null && pathMapping.containsKey(pathCode);
    }
    @Override
    public double calculateDistance(String startCode, String endCode) {
        int[] startCoord = JsonUtils.getCoordinate(startCode, pathMapping);
        int[] endCoord = JsonUtils.getCoordinate(endCode, pathMapping);
        if (startCoord == null || endCoord == null) {
            return -1;
        }
        return JsonUtils.calculateManhattanDistance(startCoord, endCoord);
    }
    @Override
    public Map<String, Map<String, Integer>> getPathMapping() {
        return pathMapping;
    }
    @Override
    public List<Map<String, String>> getNeighbors(String pathCode) {
        return adjacencyList.getOrDefault(pathCode, new ArrayList<>());
    }
    /**
     * 构建邻接表
     */
    private void buildAdjacencyList() {
        // 创建坐标到编号的临时映射
        Map<String, String> tempCoordToCode = new HashMap<>();
        for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) {
            String code = entry.getKey();
            Map<String, Integer> coordMap = entry.getValue();
            if (coordMap.containsKey("x") && coordMap.containsKey("y")) {
                String coordKey = coordMap.get("x") + "," + coordMap.get("y");
                tempCoordToCode.put(coordKey, code);
            }
        }
        // 四个方向:上、右、下、左
        int[][] directions = {{0, -1}, {1, 0}, {0, 1}, {-1, 0}};
        String[] directionAngles = {"270", "0", "90", "180"};
        for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) {
            String code = entry.getKey();
            Map<String, Integer> coordMap = entry.getValue();
            if (!coordMap.containsKey("x") || !coordMap.containsKey("y")) {
                continue;
            }
            int x = coordMap.get("x");
            int y = coordMap.get("y");
            List<Map<String, String>> neighbors = new ArrayList<>();
            // 检查四个方向的邻居
            for (int i = 0; i < directions.length; i++) {
                int newX = x + directions[i][0];
                int newY = y + directions[i][1];
                String coordKey = newX + "," + newY;
                String neighborCode = tempCoordToCode.get(coordKey);
                if (neighborCode != null) {
                    Map<String, String> neighbor = new HashMap<>();
                    neighbor.put("code", neighborCode);
                    neighbor.put("direction", directionAngles[i]);
                    neighbors.add(neighbor);
                }
            }
            adjacencyList.put(code, neighbors);
        }
    }
    /**
     * 时空A*节点类
     */
    private static class SpaceTimeAStarNode {
        final String code;
        final int[] coordinates;
        final long timePoint;
        final double gScore;
        final double fScore;
        public SpaceTimeAStarNode(String code, int[] coordinates, long timePoint,
                                  double gScore, double fScore) {
            this.code = code;
            this.coordinates = coordinates;
            this.timePoint = timePoint;
            this.gScore = gScore;
            this.fScore = fScore;
        }
    }
    /**
     * 约束检查器
     */
    private static class EnhancedConstraintChecker {
        private final List<double[]> staticConstraints;
        private final Map<String, String> spaceTimeOccupancyMap;
        private final Map<String, Map<String, Integer>> pathMapping;
        private final CTUPhysicalConfig physicalConfig;
        public EnhancedConstraintChecker(List<double[]> staticConstraints,
                                         Map<String, String> spaceTimeOccupancyMap,
                                         Map<String, Map<String, Integer>> pathMapping,
                                         CTUPhysicalConfig physicalConfig) {
            this.staticConstraints = staticConstraints;
            this.spaceTimeOccupancyMap = spaceTimeOccupancyMap != null ? spaceTimeOccupancyMap : new HashMap<>();
            this.pathMapping = pathMapping;
            this.physicalConfig = physicalConfig;
        }
        /**
         * 检查移动是否有效
         */
        public boolean isValidMove(String fromCode, String toCode, long fromTime, long toTime) {
            // 检查静态约束
            if (!isValidStaticConstraint(toCode)) {
                return false;
            }
            // 检查时空占用
            if (!isValidSpaceTimeConstraint(toCode, toTime)) {
                return false;
            }
            // 检查物理约束
            return isValidPhysicalConstraint(fromCode, toCode, fromTime, toTime);
        }
        /**
         * 检查等待是否有效
         */
        public boolean isValidWait(String code, long fromTime, long toTime) {
            // 检查时空占用期间是否有冲突
            for (long t = fromTime; t <= toTime; t += 100) { // 100ms检查间隔
                if (!isValidSpaceTimeConstraint(code, t)) {
                    return false;
                }
            }
            return true;
        }
        private boolean isValidStaticConstraint(String pathCode) {
            if (staticConstraints == null || staticConstraints.isEmpty()) {
                return true;
            }
            int[] coord = JsonUtils.getCoordinate(pathCode, pathMapping);
            if (coord == null) {
                return false;
            }
            for (double[] constraint : staticConstraints) {
                double cx = constraint[0];
                double cy = constraint[1];
                double radius = constraint[2];
                double distance = Math.sqrt(Math.pow(coord[0] - cx, 2) + Math.pow(coord[1] - cy, 2));
                if (distance <= radius) {
                    return false;
                }
            }
            return true;
        }
        private boolean isValidSpaceTimeConstraint(String pathCode, long timePoint) {
            int[] coord = JsonUtils.getCoordinate(pathCode, pathMapping);
            if (coord == null) {
                return false;
            }
            // 检查精确的时空占用
            String spaceTimeKey = coord[0] + "," + coord[1] + "," + (timePoint / 1000);
            if (spaceTimeOccupancyMap.containsKey(spaceTimeKey)) {
                return false;
            }
            // 检查周围时间段的占用(考虑安全间隔)
            long safetyMargin = (long) (physicalConfig.getMinSafetyDistance() * 1000 / physicalConfig.getNormalSpeed());
            for (long t = timePoint - safetyMargin; t <= timePoint + safetyMargin; t += 100) {
                String key = coord[0] + "," + coord[1] + "," + (t / 1000);
                if (spaceTimeOccupancyMap.containsKey(key)) {
                    return false;
                }
            }
            return true;
        }
        private boolean isValidPhysicalConstraint(String fromCode, String toCode, long fromTime, long toTime) {
            // 物理约束检查
            long timeDiff = toTime - fromTime;
            double minTimeRequired = physicalConfig.getStandardPointDistance() / physicalConfig.getMaxSpeed() * 1000;
            // 时间不能小于物理最小时间
            return timeDiff >= minTimeRequired;
        }
    }
    /**
     * 快速路径规划 - 先尝试简化空间路径规划
     * 对于近距离路径,直接返回结果避免复杂的时空计算
     */
    private PlannedPath tryFastPathPlanning(String startCode, String endCode, List<double[]> constraints) {
        // 检查缓存
        String cacheKey = startCode + "->" + endCode;
        if (fastPathCache.containsKey(cacheKey)) {
            List<String> cachedPath = fastPathCache.get(cacheKey);
            return convertToPlannedPath(cachedPath);
        }
        // 计算距离,如果距离较近则使用快速算法
        double distance = getCachedDistance(startCode, endCode);
        if (distance > 0 && distance <= 40) { // 40个单位以内使用快速算法
            List<String> fastPath = simpleAStarSearch(startCode, endCode, constraints);
            if (fastPath != null && !fastPath.isEmpty()) {
                fastPathCache.put(cacheKey, fastPath); // 缓存结果
                return convertToPlannedPath(fastPath);
            }
        }
        return null; // 快速路径失败,需要使用完整算法
    }
    /**
     * 简化A*搜索
     */
    private List<String> simpleAStarSearch(String startCode, String endCode, List<double[]> constraints) {
        int[] startCoord = JsonUtils.getCoordinate(startCode, pathMapping);
        int[] endCoord = JsonUtils.getCoordinate(endCode, pathMapping);
        if (startCoord == null || endCoord == null) {
            return null;
        }
        PriorityQueue<SimpleAStarNode> openSet = new PriorityQueue<>(
                Comparator.comparingDouble(n -> n.fScore)
        );
        Set<String> closedSet = new HashSet<>();
        Map<String, Double> gScores = new HashMap<>();
        Map<String, SimpleAStarNode> cameFrom = new HashMap<>();
        SimpleAStarNode startNode = new SimpleAStarNode(
                startCode, startCoord, 0, spatialHeuristic(startCoord, endCoord)
        );
        openSet.offer(startNode);
        gScores.put(startCode, 0.0);
        // 简化的约束检查器
        FastConstraintChecker constraintChecker = new FastConstraintChecker(constraints);
        int searchDepth = 0;
        long searchStartTime = System.currentTimeMillis();
        while (!openSet.isEmpty() && searchDepth < 3000) {
            if (System.currentTimeMillis() - searchStartTime > 5000) { // 5秒超时
                break;
            }
            SimpleAStarNode current = openSet.poll();
            if (closedSet.contains(current.code)) {
                continue;
            }
            closedSet.add(current.code);
            searchDepth++;
            if (current.code.equals(endCode)) {
                return reconstructSimplePath(cameFrom, startNode, current);
            }
            // 扩展邻居
            expandSimpleNeighbors(current, endCoord, openSet, closedSet, gScores, cameFrom, constraintChecker);
        }
        return null;
    }
    /**
     * 预计算常用距离
     */
    private void precomputeCommonDistances() {
        System.out.println("距离缓存初始化完成");
    }
    /**
     * 获取缓存的距离
     */
    private double getCachedDistance(String from, String to) {
        String key = from + "-" + to;
        return distanceCache.computeIfAbsent(key, k -> {
            int[] coord1 = JsonUtils.getCoordinate(from, pathMapping);
            int[] coord2 = JsonUtils.getCoordinate(to, pathMapping);
            if (coord1 != null && coord2 != null) {
                return JsonUtils.calculateManhattanDistance(coord1, coord2);
            }
            return -1.0;
        });
    }
    /**
     * 空间启发式函数
     */
    private double spatialHeuristic(int[] coord1, int[] coord2) {
        return Math.abs(coord1[0] - coord2[0]) + Math.abs(coord1[1] - coord2[1]);
    }
    /**
     * 扩展邻居节点
     */
    private void expandSimpleNeighbors(SimpleAStarNode current, int[] endCoord,
                                       PriorityQueue<SimpleAStarNode> openSet,
                                       Set<String> closedSet, Map<String, Double> gScores,
                                       Map<String, SimpleAStarNode> cameFrom,
                                       FastConstraintChecker constraintChecker) {
        List<Map<String, String>> neighbors = getNeighbors(current.code);
        for (Map<String, String> neighbor : neighbors) {
            String neighborCode = neighbor.get("code");
            if (closedSet.contains(neighborCode)) {
                continue;
            }
            if (!constraintChecker.isValidPosition(neighborCode)) {
                continue;
            }
            double edgeCost = getCachedDistance(current.code, neighborCode);
            if (edgeCost < 0) edgeCost = 1.0; // 默认边权重
            double gScore = gScores.get(current.code) + edgeCost;
            if (!gScores.containsKey(neighborCode) || gScore < gScores.get(neighborCode)) {
                gScores.put(neighborCode, gScore);
                int[] neighborCoord = JsonUtils.getCoordinate(neighborCode, pathMapping);
                if (neighborCoord != null) {
                    SimpleAStarNode neighborNode = new SimpleAStarNode(
                            neighborCode, neighborCoord, gScore,
                            gScore + spatialHeuristic(neighborCoord, endCoord)
                    );
                    cameFrom.put(neighborCode, current);
                    openSet.offer(neighborNode);
                }
            }
        }
    }
    /**
     * 重建简化路径
     */
    private List<String> reconstructSimplePath(Map<String, SimpleAStarNode> cameFrom,
                                               SimpleAStarNode startNode, SimpleAStarNode endNode) {
        List<String> path = new ArrayList<>();
        SimpleAStarNode current = endNode;
        while (current != null) {
            path.add(0, current.code);
            current = cameFrom.get(current.code);
        }
        return path;
    }
    /**
     * 将路径转换为PlannedPath
     */
    private PlannedPath convertToPlannedPath(List<String> pathCodes) {
        if (pathCodes == null || pathCodes.isEmpty()) {
            return null;
        }
        List<PathCode> codeList = new ArrayList<>();
        for (int i = 0; i < pathCodes.size(); i++) {
            String position = pathCodes.get(i);
            String direction = "90"; // 默认方向
            // 计算方向
            if (i < pathCodes.size() - 1) {
                direction = calculateDirection(position, pathCodes.get(i + 1));
            }
            PathCode pathCode = new PathCode(position, direction);
            codeList.add(pathCode);
        }
        return new PlannedPath("", "", codeList);
    }
    /**
     * A*节点类
     */
    private static class SimpleAStarNode {
        final String code;
        final int[] coordinates;
        final double gScore;
        final double fScore;
        public SimpleAStarNode(String code, int[] coordinates, double gScore, double fScore) {
            this.code = code;
            this.coordinates = coordinates;
            this.gScore = gScore;
            this.fScore = fScore;
        }
    }
    /**
     * 约束检查器
     */
    private class FastConstraintChecker {
        private final Set<String> blockedPositions;
        public FastConstraintChecker(List<double[]> constraints) {
            this.blockedPositions = new HashSet<>();
            precomputeBlockedPositions(constraints);
        }
        private void precomputeBlockedPositions(List<double[]> constraints) {
            if (constraints == null || constraints.isEmpty()) {
                return;
            }
            for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) {
                String pathCode = entry.getKey();
                int[] coord = JsonUtils.getCoordinate(pathCode, pathMapping);
                if (coord != null && isBlocked(coord, constraints)) {
                    blockedPositions.add(pathCode);
                }
            }
        }
        private boolean isBlocked(int[] coord, List<double[]> constraints) {
            for (double[] constraint : constraints) {
                double cx = constraint[0];
                double cy = constraint[1];
                double radius = constraint[2];
                double distance = Math.sqrt(Math.pow(coord[0] - cx, 2) + Math.pow(coord[1] - cy, 2));
                if (distance <= radius) {
                    return true;
                }
            }
            return false;
        }
        public boolean isValidPosition(String pathCode) {
            return !blockedPositions.contains(pathCode);
        }
    }
}
algo-zkd/src/main/java/com/algo/service/CollisionDetector.java
New file
@@ -0,0 +1,567 @@
package com.algo.service;
import com.algo.model.CTUPhysicalConfig;
import com.algo.model.Conflict;
import com.algo.model.PathCode;
import com.algo.model.PlannedPath;
import com.algo.util.JsonUtils;
import java.util.*;
/**
 * 碰撞检测器(增强版)
 * 用于检测CTU路径之间的时空冲突,支持物理参数和高精度时间检测
 */
public class CollisionDetector {
    /**
     * 路径映射表
     */
    private final Map<String, Map<String, Integer>> pathMapping;
    /**
     * 默认最小安全距离
     */
    private final double defaultMinDistance;
    /**
     * 时间精度(毫秒)
     */
    private final long timeResolution = 100; // 100毫秒的时间精度
    /**
     * 空间分辨率(米)
     */
    private final double spaceResolution = 0.1; // 10厘米的空间精度
    /**
     * 构造函数
     *
     * @param pathMapping        路径映射表
     * @param defaultMinDistance 默认最小安全距离
     */
    public CollisionDetector(Map<String, Map<String, Integer>> pathMapping,
                             double defaultMinDistance) {
        this.pathMapping = pathMapping;
        this.defaultMinDistance = defaultMinDistance;
    }
    /**
     * 构造函数(使用默认参数)
     *
     * @param pathMapping 路径映射表
     */
    public CollisionDetector(Map<String, Map<String, Integer>> pathMapping) {
        this(pathMapping, 3.0);
    }
    /**
     * 检测路径冲突(增强版,支持CTU物理参数)
     *
     * @param plannedPaths 规划路径列表
     * @return 冲突列表
     */
    public List<Conflict> detectConflicts(List<PlannedPath> plannedPaths) {
        List<Conflict> conflicts = new ArrayList<>();
        if (plannedPaths == null || plannedPaths.size() < 2) {
            return conflicts;
        }
        System.out.println("开始增强版碰撞检测,路径数量: " + plannedPaths.size());
        // 构建高精度时空表
        Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable = buildEnhancedSpaceTimeTable(plannedPaths);
        // 检测顶点冲突(同一时间同一位置)
        List<Conflict> vertexConflicts = detectEnhancedVertexConflicts(spaceTimeTable);
        conflicts.addAll(vertexConflicts);
        System.out.println("顶点冲突数量: " + vertexConflicts.size());
        // 检测边冲突(CTU交换位置)
        List<Conflict> edgeConflicts = detectEnhancedEdgeConflicts(spaceTimeTable);
        conflicts.addAll(edgeConflicts);
        System.out.println("边冲突数量: " + edgeConflicts.size());
        // 检测跟随冲突(CTU距离过近,考虑物理参数)
        List<Conflict> followingConflicts = detectEnhancedFollowingConflicts(spaceTimeTable);
        conflicts.addAll(followingConflicts);
        System.out.println("跟随冲突数量: " + followingConflicts.size());
        // 检测物理尺寸冲突
        List<Conflict> physicalConflicts = detectPhysicalSizeConflicts(spaceTimeTable);
        conflicts.addAll(physicalConflicts);
        System.out.println("物理尺寸冲突数量: " + physicalConflicts.size());
        System.out.println("增强版碰撞检测完成,总冲突数量: " + conflicts.size());
        return conflicts;
    }
    /**
     * 构建增强的时空表(高精度时间和物理参数)
     *
     * @param plannedPaths 规划路径列表
     * @return 增强时空表
     */
    private Map<Long, List<EnhancedSpaceTimeNode>> buildEnhancedSpaceTimeTable(List<PlannedPath> plannedPaths) {
        Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable = new HashMap<>();
        for (PlannedPath path : plannedPaths) {
            String agvId = path.getAgvId();
            List<PathCode> codeList = path.getCodeList();
            if (codeList == null || codeList.isEmpty()) {
                continue;
            }
            // 获取CTU的物理配置(这里需要从某个地方获取,可能需要扩展PlannedPath类)
            CTUPhysicalConfig physicalConfig = getPhysicalConfigForCTU(agvId);
            long currentTime = System.currentTimeMillis(); // 基准时间
            for (int i = 0; i < codeList.size(); i++) {
                PathCode pathCode = codeList.get(i);
                String position = pathCode.getCode();
                int[] coordinates = JsonUtils.getCoordinate(position, pathMapping);
                if (coordinates != null) {
                    // 计算精确的到达时间
                    long arrivalTime = calculateArrivalTime(currentTime, i, codeList, physicalConfig);
                    // 计算停留时间
                    long departureTime = calculateDepartureTime(arrivalTime, pathCode, physicalConfig);
                    // 创建时间段内的多个时间点
                    for (long timePoint = arrivalTime; timePoint <= departureTime; timePoint += timeResolution) {
                        EnhancedSpaceTimeNode node = new EnhancedSpaceTimeNode(
                                agvId, position, coordinates, timePoint, arrivalTime, departureTime, physicalConfig
                        );
                        spaceTimeTable.computeIfAbsent(timePoint, k -> new ArrayList<>()).add(node);
                    }
                }
            }
        }
        return spaceTimeTable;
    }
    /**
     * 获取CTU的物理配置
     *
     * @param agvId CTU编号
     * @return 物理配置
     */
    private CTUPhysicalConfig getPhysicalConfigForCTU(String agvId) {
        // 这里应该从配置管理器或数据库获取实际的CTU物理参数
        // 暂时返回默认配置
        CTUPhysicalConfig config = new CTUPhysicalConfig();
        config.setMaxSpeed(2.0);
        config.setNormalSpeed(1.5);
        config.setMaxAcceleration(1.0);
        config.setMaxDeceleration(1.5);
        config.setTurnTime90(2.0);
        config.setTurnTime180(4.0);
        config.setMinSafetyDistance(defaultMinDistance);
        config.setMinFollowingDistance(2.0);
        config.setCtuLength(1.2);
        config.setCtuWidth(0.8);
        config.setStartupTime(1.0);
        config.setStopTime(0.5);
        config.setStandardPointDistance(1.0);
        return config;
    }
    /**
     * 计算到达时间
     *
     * @param startTime 起始时间
     * @param pathIndex 路径索引
     * @param codeList  路径代码列表
     * @param config    物理配置
     * @return 到达时间
     */
    private long calculateArrivalTime(long startTime, int pathIndex, List<PathCode> codeList, CTUPhysicalConfig config) {
        if (pathIndex == 0) {
            return startTime;
        }
        long travelTime = 0;
        for (int i = 1; i <= pathIndex; i++) {
            PathCode currentCode = codeList.get(i);
            PathCode previousCode = codeList.get(i - 1);
            // 计算移动时间
            double distance = config.getStandardPointDistance();
            double speed = config.getNormalSpeed();
            travelTime += (long) ((distance / speed) * 1000); // 转换为毫秒
            // 如果有方向变化,增加转向时间
            if (!currentCode.getDirection().equals(previousCode.getDirection())) {
                double turnTime = calculateTurnTime(previousCode.getDirection(), currentCode.getDirection(), config);
                travelTime += (long) (turnTime * 1000);
            }
            // 考虑加速和减速时间
            if (i == 1) {
                travelTime += (long) (config.getStartupTime() * 1000);
            }
        }
        return startTime + travelTime;
    }
    /**
     * 计算离开时间
     *
     * @param arrivalTime 到达时间
     * @param pathCode    路径代码
     * @param config      物理配置
     * @return 离开时间
     */
    private long calculateDepartureTime(long arrivalTime, PathCode pathCode, CTUPhysicalConfig config) {
        long stayTime = 500; // 默认停留时间500毫秒
        // 根据路径代码类型确定停留时间
        if (pathCode.isTargetPoint()) {
            if ("1".equals(pathCode.getPosType())) { // 取货
                stayTime = 3000; // 取货需要3秒
            } else if ("2".equals(pathCode.getPosType())) { // 放货
                stayTime = 2000; // 放货需要2秒
            }
        }
        return arrivalTime + stayTime;
    }
    /**
     * 计算转向时间
     *
     * @param fromDirection 起始方向
     * @param toDirection   目标方向
     * @param config        物理配置
     * @return 转向时间(秒)
     */
    private double calculateTurnTime(String fromDirection, String toDirection, CTUPhysicalConfig config) {
        if (fromDirection.equals(toDirection)) {
            return 0.0;
        }
        // 简化的方向转换计算
        // 实际实现中需要根据具体的方向编码计算角度差
        int angleDiff = Math.abs(Integer.parseInt(toDirection) - Integer.parseInt(fromDirection));
        angleDiff = Math.min(angleDiff, 4 - angleDiff); // 考虑环形
        switch (angleDiff) {
            case 1:
                return config.getTurnTime90();
            case 2:
                return config.getTurnTime180();
            default:
                return config.getTurnTime90() * angleDiff;
        }
    }
    /**
     * 检测增强的顶点冲突
     *
     * @param spaceTimeTable 时空表
     * @return 顶点冲突列表
     */
    private List<Conflict> detectEnhancedVertexConflicts(Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable) {
        List<Conflict> conflicts = new ArrayList<>();
        for (Map.Entry<Long, List<EnhancedSpaceTimeNode>> entry : spaceTimeTable.entrySet()) {
            long timePoint = entry.getKey();
            List<EnhancedSpaceTimeNode> nodes = entry.getValue();
            // 按位置分组
            Map<String, List<EnhancedSpaceTimeNode>> positionGroups = new HashMap<>();
            for (EnhancedSpaceTimeNode node : nodes) {
                positionGroups.computeIfAbsent(node.position, k -> new ArrayList<>()).add(node);
            }
            // 检查每个位置是否有多个CTU
            for (Map.Entry<String, List<EnhancedSpaceTimeNode>> posEntry : positionGroups.entrySet()) {
                String position = posEntry.getKey();
                List<EnhancedSpaceTimeNode> ctuNodes = posEntry.getValue();
                if (ctuNodes.size() > 1) {
                    // 进一步检查时间重叠
                    for (int i = 0; i < ctuNodes.size(); i++) {
                        for (int j = i + 1; j < ctuNodes.size(); j++) {
                            EnhancedSpaceTimeNode node1 = ctuNodes.get(i);
                            EnhancedSpaceTimeNode node2 = ctuNodes.get(j);
                            // 检查时间段是否重叠
                            if (timeRangeOverlap(node1.arrivalTime, node1.departureTime,
                                    node2.arrivalTime, node2.departureTime)) {
                                Conflict conflict = new Conflict(
                                        "enhanced_vertex",
                                        node1.agvId,
                                        node2.agvId,
                                        (int) (timePoint / 1000), // 转换为秒用于显示
                                        position,
                                        position,
                                        String.format("CTU %s 和 %s 在时间 %d 占用同一位置 %s(时间重叠)",
                                                node1.agvId, node2.agvId, timePoint / 1000, position)
                                );
                                conflicts.add(conflict);
                            }
                        }
                    }
                }
            }
        }
        return conflicts;
    }
    /**
     * 检测增强的边冲突
     *
     * @param spaceTimeTable 时空表
     * @return 边冲突列表
     */
    private List<Conflict> detectEnhancedEdgeConflicts(Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable) {
        List<Conflict> conflicts = new ArrayList<>();
        if (spaceTimeTable.isEmpty()) {
            return conflicts;
        }
        List<Long> sortedTimes = new ArrayList<>(spaceTimeTable.keySet());
        Collections.sort(sortedTimes);
        for (int i = 0; i < sortedTimes.size() - 1; i++) {
            long currentTime = sortedTimes.get(i);
            long nextTime = sortedTimes.get(i + 1);
            // 只检查连续的时间点
            if (nextTime - currentTime <= timeResolution * 2) {
                List<EnhancedSpaceTimeNode> currentNodes = spaceTimeTable.get(currentTime);
                List<EnhancedSpaceTimeNode> nextNodes = spaceTimeTable.get(nextTime);
                conflicts.addAll(detectPositionSwapping(currentNodes, nextNodes, currentTime, nextTime));
            }
        }
        return conflicts;
    }
    /**
     * 检测位置交换
     *
     * @param currentNodes 当前时间的节点
     * @param nextNodes    下一时间的节点
     * @param currentTime  当前时间
     * @param nextTime     下一时间
     * @return 冲突列表
     */
    private List<Conflict> detectPositionSwapping(List<EnhancedSpaceTimeNode> currentNodes,
                                                  List<EnhancedSpaceTimeNode> nextNodes,
                                                  long currentTime, long nextTime) {
        List<Conflict> conflicts = new ArrayList<>();
        Map<String, String> currentPositions = new HashMap<>();
        Map<String, String> nextPositions = new HashMap<>();
        for (EnhancedSpaceTimeNode node : currentNodes) {
            currentPositions.put(node.agvId, node.position);
        }
        for (EnhancedSpaceTimeNode node : nextNodes) {
            nextPositions.put(node.agvId, node.position);
        }
        for (Map.Entry<String, String> entry1 : currentPositions.entrySet()) {
            String ctu1 = entry1.getKey();
            String pos1Current = entry1.getValue();
            for (Map.Entry<String, String> entry2 : currentPositions.entrySet()) {
                String ctu2 = entry2.getKey();
                String pos2Current = entry2.getValue();
                if (ctu1.compareTo(ctu2) >= 0) {
                    continue;
                }
                String pos1Next = nextPositions.get(ctu1);
                String pos2Next = nextPositions.get(ctu2);
                if (pos1Next != null && pos2Next != null &&
                        pos1Current.equals(pos2Next) && pos2Current.equals(pos1Next)) {
                    Conflict conflict = new Conflict(
                            "enhanced_edge",
                            ctu1,
                            ctu2,
                            (int) (currentTime / 1000),
                            pos1Current,
                            pos2Current,
                            String.format("CTU %s 和 %s 在时间 %d-%d 交换位置 %s<->%s",
                                    ctu1, ctu2, currentTime / 1000, nextTime / 1000, pos1Current, pos2Current)
                    );
                    conflicts.add(conflict);
                }
            }
        }
        return conflicts;
    }
    /**
     * 检测增强的跟随冲突(考虑CTU物理参数)
     *
     * @param spaceTimeTable 时空表
     * @return 跟随冲突列表
     */
    private List<Conflict> detectEnhancedFollowingConflicts(Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable) {
        List<Conflict> conflicts = new ArrayList<>();
        for (Map.Entry<Long, List<EnhancedSpaceTimeNode>> entry : spaceTimeTable.entrySet()) {
            long timePoint = entry.getKey();
            List<EnhancedSpaceTimeNode> nodes = entry.getValue();
            for (int i = 0; i < nodes.size(); i++) {
                for (int j = i + 1; j < nodes.size(); j++) {
                    EnhancedSpaceTimeNode node1 = nodes.get(i);
                    EnhancedSpaceTimeNode node2 = nodes.get(j);
                    double distance = calculateDistance(node1.coordinates, node2.coordinates);
                    double minSafeDistance = Math.max(
                            node1.physicalConfig.getMinFollowingDistance(),
                            node2.physicalConfig.getMinFollowingDistance()
                    );
                    if (distance > 0 && distance < minSafeDistance) {
                        Conflict conflict = new Conflict(
                                "enhanced_follow",
                                node1.agvId,
                                node2.agvId,
                                (int) (timePoint / 1000),
                                node1.position,
                                node2.position,
                                String.format("CTU %s 和 %s 在时间 %d 距离过近 (%.2f < %.2f)",
                                        node1.agvId, node2.agvId, timePoint / 1000, distance, minSafeDistance)
                        );
                        conflicts.add(conflict);
                    }
                }
            }
        }
        return conflicts;
    }
    /**
     * 检测物理尺寸冲突
     *
     * @param spaceTimeTable 时空表
     * @return 物理冲突列表
     */
    private List<Conflict> detectPhysicalSizeConflicts(Map<Long, List<EnhancedSpaceTimeNode>> spaceTimeTable) {
        List<Conflict> conflicts = new ArrayList<>();
        for (Map.Entry<Long, List<EnhancedSpaceTimeNode>> entry : spaceTimeTable.entrySet()) {
            long timePoint = entry.getKey();
            List<EnhancedSpaceTimeNode> nodes = entry.getValue();
            for (int i = 0; i < nodes.size(); i++) {
                for (int j = i + 1; j < nodes.size(); j++) {
                    EnhancedSpaceTimeNode node1 = nodes.get(i);
                    EnhancedSpaceTimeNode node2 = nodes.get(j);
                    if (checkPhysicalCollision(node1, node2)) {
                        Conflict conflict = new Conflict(
                                "physical_size",
                                node1.agvId,
                                node2.agvId,
                                (int) (timePoint / 1000),
                                node1.position,
                                node2.position,
                                String.format("CTU %s 和 %s 在时间 %d 发生物理尺寸冲突",
                                        node1.agvId, node2.agvId, timePoint / 1000)
                        );
                        conflicts.add(conflict);
                    }
                }
            }
        }
        return conflicts;
    }
    /**
     * 检查两个CTU是否发生物理碰撞
     *
     * @param node1 CTU节点1
     * @param node2 CTU节点2
     * @return 是否碰撞
     */
    private boolean checkPhysicalCollision(EnhancedSpaceTimeNode node1, EnhancedSpaceTimeNode node2) {
        double distance = calculateDistance(node1.coordinates, node2.coordinates);
        // 考虑CTU的物理尺寸
        double ctu1Radius = Math.max(node1.physicalConfig.getCtuLength(), node1.physicalConfig.getCtuWidth()) / 2;
        double ctu2Radius = Math.max(node2.physicalConfig.getCtuLength(), node2.physicalConfig.getCtuWidth()) / 2;
        double minClearance = ctu1Radius + ctu2Radius + spaceResolution;
        return distance > 0 && distance < minClearance;
    }
    /**
     * 检查时间范围是否重叠
     *
     * @param start1 开始时间1
     * @param end1   结束时间1
     * @param start2 开始时间2
     * @param end2   结束时间2
     * @return 是否重叠
     */
    private boolean timeRangeOverlap(long start1, long end1, long start2, long end2) {
        return Math.max(start1, start2) < Math.min(end1, end2);
    }
    /**
     * 计算两个坐标之间的距离
     *
     * @param coord1 坐标1
     * @param coord2 坐标2
     * @return 距离值
     */
    private double calculateDistance(int[] coord1, int[] coord2) {
        if (coord1 == null || coord2 == null || coord1.length != 2 || coord2.length != 2) {
            return -1;
        }
        return Math.sqrt(Math.pow(coord1[0] - coord2[0], 2) + Math.pow(coord1[1] - coord2[1], 2));
    }
    /**
     * 增强的时空节点内部类
     */
    private static class EnhancedSpaceTimeNode {
        final String agvId;
        final String position;
        final int[] coordinates;
        final long timePoint;
        final long arrivalTime;
        final long departureTime;
        final CTUPhysicalConfig physicalConfig;
        public EnhancedSpaceTimeNode(String agvId, String position, int[] coordinates,
                                     long timePoint, long arrivalTime, long departureTime,
                                     CTUPhysicalConfig physicalConfig) {
            this.agvId = agvId;
            this.position = position;
            this.coordinates = coordinates;
            this.timePoint = timePoint;
            this.arrivalTime = arrivalTime;
            this.departureTime = departureTime;
            this.physicalConfig = physicalConfig;
        }
    }
}
algo-zkd/src/main/java/com/algo/service/CollisionResolver.java
New file
@@ -0,0 +1,306 @@
package com.algo.service;
import com.algo.model.Conflict;
import com.algo.model.ExecutingTask;
import com.algo.model.PathCode;
import com.algo.model.PlannedPath;
import java.util.*;
/**
 * 碰撞解决器
 * 用于解决AGV路径之间的冲突
 */
public class CollisionResolver {
    /**
     * 碰撞检测器
     */
    private final CollisionDetector collisionDetector;
    /**
     * 构造函数
     *
     * @param collisionDetector 碰撞检测器
     */
    public CollisionResolver(CollisionDetector collisionDetector) {
        this.collisionDetector = collisionDetector;
    }
    /**
     * 解决路径冲突
     *
     * @param plannedPaths   规划路径列表
     * @param conflicts      冲突列表
     * @param executingTasks 执行中任务列表
     * @return 解决冲突后的路径列表
     */
    public List<PlannedPath> resolveConflicts(List<PlannedPath> plannedPaths,
                                              List<Conflict> conflicts,
                                              List<ExecutingTask> executingTasks) {
        if (conflicts == null || conflicts.isEmpty()) {
            return plannedPaths;
        }
        System.out.println("开始解决 " + conflicts.size() + " 个路径冲突");
        // 构建路径字典便于快速访问
        Map<String, PlannedPath> pathsMap = new HashMap<>();
        for (PlannedPath path : plannedPaths) {
            pathsMap.put(path.getAgvId(), path);
        }
        // 按时间步排序处理冲突
        List<Conflict> sortedConflicts = new ArrayList<>(conflicts);
        sortedConflicts.sort(Comparator.comparingInt(Conflict::getTimeStep));
        // 逐个解决冲突
        for (Conflict conflict : sortedConflicts) {
            resolveSingleConflict(pathsMap, conflict, executingTasks);
        }
        List<PlannedPath> resolvedPaths = new ArrayList<>(pathsMap.values());
        // 验证解决结果
        List<Conflict> remainingConflicts = collisionDetector.detectConflicts(resolvedPaths);
        System.out.println("冲突解决完成,剩余冲突数量: " + remainingConflicts.size());
        return resolvedPaths;
    }
    /**
     * 解决单个冲突
     *
     * @param pathsMap       路径映射
     * @param conflict       冲突
     * @param executingTasks 执行中任务列表
     */
    private void resolveSingleConflict(Map<String, PlannedPath> pathsMap,
                                       Conflict conflict,
                                       List<ExecutingTask> executingTasks) {
        String conflictType = conflict.getType();
        switch (conflictType) {
            case "vertex":
                resolveVertexConflict(pathsMap, conflict, executingTasks);
                break;
            case "edge":
                resolveEdgeConflict(pathsMap, conflict, executingTasks);
                break;
            case "follow":
                resolveFollowingConflict(pathsMap, conflict, executingTasks);
                break;
            default:
                System.out.println("未知冲突类型: " + conflictType);
        }
    }
    /**
     * 解决顶点冲突
     *
     * @param pathsMap       路径映射
     * @param conflict       冲突
     * @param executingTasks 执行中任务列表
     */
    private void resolveVertexConflict(Map<String, PlannedPath> pathsMap,
                                       Conflict conflict,
                                       List<ExecutingTask> executingTasks) {
        String agv1 = conflict.getAgv1();
        String agv2 = conflict.getAgv2();
        int timeStep = conflict.getTimeStep();
        // 评估AGV优先级
        AGVPriority priority1 = evaluateAgvPriority(agv1, pathsMap.get(agv1), executingTasks);
        AGVPriority priority2 = evaluateAgvPriority(agv2, pathsMap.get(agv2), executingTasks);
        // 优先级低的AGV进行延迟
        if (priority1.priorityScore >= priority2.priorityScore) {
            // agv2优先级低,延迟agv2
            addDelayToPath(pathsMap.get(agv2), timeStep, 1);
            System.out.println("为解决顶点冲突,AGV " + agv2 + " 在时间步 " + timeStep + " 延迟1步");
        } else {
            // agv1优先级低,延迟agv1
            addDelayToPath(pathsMap.get(agv1), timeStep, 1);
            System.out.println("为解决顶点冲突,AGV " + agv1 + " 在时间步 " + timeStep + " 延迟1步");
        }
    }
    /**
     * 解决边冲突
     *
     * @param pathsMap       路径映射
     * @param conflict       冲突
     * @param executingTasks 执行中任务列表
     */
    private void resolveEdgeConflict(Map<String, PlannedPath> pathsMap,
                                     Conflict conflict,
                                     List<ExecutingTask> executingTasks) {
        String agv1 = conflict.getAgv1();
        String agv2 = conflict.getAgv2();
        int timeStep = conflict.getTimeStep();
        // 评估AGV优先级
        AGVPriority priority1 = evaluateAgvPriority(agv1, pathsMap.get(agv1), executingTasks);
        AGVPriority priority2 = evaluateAgvPriority(agv2, pathsMap.get(agv2), executingTasks);
        // 优先级低的AGV进行延迟
        if (priority1.priorityScore >= priority2.priorityScore) {
            // agv2优先级低,延迟agv2
            addDelayToPath(pathsMap.get(agv2), timeStep, 2);
            System.out.println("为解决边冲突,AGV " + agv2 + " 在时间步 " + timeStep + " 延迟2步");
        } else {
            // agv1优先级低,延迟agv1
            addDelayToPath(pathsMap.get(agv1), timeStep, 2);
            System.out.println("为解决边冲突,AGV " + agv1 + " 在时间步 " + timeStep + " 延迟2步");
        }
    }
    /**
     * 解决跟随冲突
     *
     * @param pathsMap       路径映射
     * @param conflict       冲突
     * @param executingTasks 执行中任务列表
     */
    private void resolveFollowingConflict(Map<String, PlannedPath> pathsMap,
                                          Conflict conflict,
                                          List<ExecutingTask> executingTasks) {
        String agv1 = conflict.getAgv1();
        String agv2 = conflict.getAgv2();
        int timeStep = conflict.getTimeStep();
        // 评估AGV优先级
        AGVPriority priority1 = evaluateAgvPriority(agv1, pathsMap.get(agv1), executingTasks);
        AGVPriority priority2 = evaluateAgvPriority(agv2, pathsMap.get(agv2), executingTasks);
        // 优先级低的AGV进行延迟
        if (priority1.priorityScore >= priority2.priorityScore) {
            // agv2优先级低,延迟agv2
            addDelayToPath(pathsMap.get(agv2), timeStep, 1);
            System.out.println("为解决跟随冲突,AGV " + agv2 + " 在时间步 " + timeStep + " 延迟1步");
        } else {
            // agv1优先级低,延迟agv1
            addDelayToPath(pathsMap.get(agv1), timeStep, 1);
            System.out.println("为解决跟随冲突,AGV " + agv1 + " 在时间步 " + timeStep + " 延迟1步");
        }
    }
    /**
     * 评估AGV优先级 - 优化版本,只保留必要字段
     *
     * @param agvId          AGV编号
     * @param path           路径
     * @param executingTasks 执行中任务列表
     * @return AGV优先级信息
     */
    private AGVPriority evaluateAgvPriority(String agvId, PlannedPath path, List<ExecutingTask> executingTasks) {
        // 查找对应的执行任务
        ExecutingTask task = null;
        for (ExecutingTask et : executingTasks) {
            if (et.getAgvId().equals(agvId)) {
                task = et;
                break;
            }
        }
        double priorityScore = 0.0;
        String explanation = "默认优先级";
        if (task != null) {
            String taskStatus = task.getTaskType();
            int taskPriority = task.getPriority();
            // 基于任务类型的优先级评分
            switch (taskStatus) {
                case "delivery":
                    priorityScore += 50; // 送货任务优先级高
                    explanation = "送货任务 - 高优先级";
                    break;
                case "pickup":
                    priorityScore += 30; // 取货任务中等优先级
                    explanation = "取货任务 - 中等优先级";
                    break;
                case "charging":
                    priorityScore += 20; // 充电任务低优先级
                    explanation = "充电任务 - 低优先级";
                    break;
                default:
                    priorityScore += 10; // 其他任务最低优先级
                    explanation = "其他任务 - 最低优先级";
            }
            // 任务优先级加成
            priorityScore += taskPriority * 10;
            // 路径长度因子(路径越短优先级越高)
            if (path != null && path.getCodeList() != null) {
                double pathLengthFactor = Math.max(0, 50 - path.getCodeList().size());
                priorityScore += pathLengthFactor;
            }
        }
        return new AGVPriority(agvId, priorityScore, explanation);
    }
    /**
     * 为路径添加延迟
     *
     * @param path       路径
     * @param timeStep   延迟开始的时间步
     * @param delaySteps 延迟步数
     */
    private void addDelayToPath(PlannedPath path, int timeStep, int delaySteps) {
        if (path == null || path.getCodeList() == null || path.getCodeList().isEmpty()) {
            return;
        }
        List<PathCode> codeList = path.getCodeList();
        // 确保时间步在有效范围内
        if (timeStep < 0 || timeStep >= codeList.size()) {
            return;
        }
        // 获取延迟位置的路径代码
        PathCode delayCode = codeList.get(timeStep);
        // 创建延迟步骤
        List<PathCode> delaySteps_list = new ArrayList<>();
        for (int i = 0; i < delaySteps; i++) {
            PathCode waitCode = new PathCode(delayCode.getCode(), delayCode.getDirection());
            waitCode.setActionType(delayCode.getActionType());
            waitCode.setTaskId(delayCode.getTaskId());
            waitCode.setPosType(delayCode.getPosType());
            waitCode.setLev(delayCode.getLev());
            waitCode.setTargetPoint(false); // 等待步骤不是目标点
            delaySteps_list.add(waitCode);
        }
        // 插入延迟步骤
        codeList.addAll(timeStep, delaySteps_list);
        // 更新路径
        path.setCodeList(codeList);
    }
    /**
     * AGV优先级评估结果内部类 - 优化版本,只保留必要字段
     */
    private static class AGVPriority {
        final String agvId;
        final double priorityScore;
        final String explanation;
        public AGVPriority(String agvId, double priorityScore, String explanation) {
            this.agvId = agvId;
            this.priorityScore = priorityScore;
            this.explanation = explanation;
        }
        @Override
        public String toString() {
            return String.format("AGV %s: 优先级=%.1f, 说明=%s",
                    agvId, priorityScore, explanation);
        }
    }
}
algo-zkd/src/main/java/com/algo/service/ExecutingTaskExtractor.java
New file
@@ -0,0 +1,478 @@
package com.algo.service;
import com.algo.model.AGVStatus;
import com.algo.model.BackpackData;
import com.algo.model.ExecutingTask;
import com.algo.model.TaskData;
import com.algo.util.JsonUtils;
import java.util.*;
/**
 * 执行中任务提取器
 * 负责从AGV状态中提取当前正在执行的任务
 */
public class ExecutingTaskExtractor {
    /**
     * 路径映射表
     */
    private Map<String, Map<String, Integer>> pathMapping;
    /**
     * 任务数据映射表
     */
    private Map<String, TaskData> taskDataMap;
    /**
     * 坐标到路径编号的映射
     */
    private Map<String, String> coordToCode;
    /**
     * 取货位置列表
     */
    private List<String> pickupPositions;
    /**
     * 充电位置列表
     */
    private List<String> chargingPositions;
    /**
     * 送货位置列表
     */
    private List<String> deliveryPositions;
    /**
     * 待机位置列表
     */
    private List<String> standbyPositions;
    /**
     * 构造函数
     *
     * @param pathMapping  路径映射表
     * @param taskDataList 任务数据列表
     */
    public ExecutingTaskExtractor(Map<String, Map<String, Integer>> pathMapping, List<TaskData> taskDataList) {
        this.pathMapping = pathMapping;
        this.coordToCode = new HashMap<>();
        this.taskDataMap = new HashMap<>();
        // 构建任务数据映射表
        if (taskDataList != null) {
            for (TaskData taskData : taskDataList) {
                taskDataMap.put(taskData.getTaskId(), taskData);
            }
        }
        // 预计算坐标映射
        buildCoordToCodeMapping();
        // 预分类位置类型
        classifyPositions();
        System.out.println("任务提取器初始化完成,分类位置:取货=" + pickupPositions.size() +
                ", 充电=" + chargingPositions.size() +
                ", 送货=" + deliveryPositions.size() +
                ", 待机=" + standbyPositions.size() +
                ", 任务数据=" + taskDataMap.size());
    }
    /**
     * 提取执行中的任务
     *
     * @param agvStatusList AGV状态列表
     * @return 执行中任务列表
     */
    public List<ExecutingTask> extractExecutingTasks(List<AGVStatus> agvStatusList) {
        List<ExecutingTask> executingTasks = new ArrayList<>();
        for (AGVStatus agvStatus : agvStatusList) {
            List<ExecutingTask> agvTasks = extractAgvTasks(agvStatus);
            executingTasks.addAll(agvTasks);
        }
        System.out.println("提取到 " + executingTasks.size() + " 个执行中任务");
        return executingTasks;
    }
    /**
     * 提取单个AGV的执行中任务
     *
     * @param agvStatus AGV状态
     * @return 执行中任务列表
     */
    private List<ExecutingTask> extractAgvTasks(AGVStatus agvStatus) {
        List<ExecutingTask> tasks = new ArrayList<>();
        // 检查AGV是否可用
        if (!agvStatus.isAvailable()) {
            return tasks;
        }
        // 分析背篓状态
        List<BackpackData> backpack = agvStatus.getBackpack();
        if (backpack == null || backpack.isEmpty()) {
            return tasks;
        }
        // 分别收集已装载和未装载的任务
        List<BackpackData> loadedTasks = new ArrayList<>();
        List<BackpackData> unloadedTasks = new ArrayList<>();
        for (BackpackData bp : backpack) {
            if (bp.getTaskId() != null && !bp.getTaskId().trim().isEmpty()) {
                TaskData taskData = getTaskData(bp.getTaskId());
                if (taskData != null) {
                    if (bp.isLoaded()) {
                        loadedTasks.add(bp);
                    } else {
                        unloadedTasks.add(bp);
                    }
                }
            }
        }
        // 确定下一步最优行动
        ExecutingTask nextTask = determineNextBestAction(agvStatus, loadedTasks, unloadedTasks);
        if (nextTask != null) {
            tasks.add(nextTask);
        }
        return tasks;
    }
    /**
     * 确定AGV下一步最优行动
     * 使用加权成本算法综合考虑距离和任务类型
     *
     * @param agvStatus     AGV状态
     * @param loadedTasks   已装载任务
     * @param unloadedTasks 未装载任务
     * @return 下一步执行任务
     */
    private ExecutingTask determineNextBestAction(AGVStatus agvStatus,
                                                  List<BackpackData> loadedTasks,
                                                  List<BackpackData> unloadedTasks) {
        String currentPosition = agvStatus.getPosition();
        List<TaskOption> taskOptions = new ArrayList<>();
        // 1. 收集所有可选的送货任务
        for (BackpackData task : loadedTasks) {
            TaskData taskData = getTaskData(task.getTaskId());
            if (taskData != null && taskData.getEnd() != null) {
                double distance = calculateDistance(currentPosition, taskData.getEnd());
                if (distance >= 0) {
                    double cost = calculateTaskCost(distance, "delivery", taskData.getPriority(), agvStatus);
                    taskOptions.add(new TaskOption(task, true, distance, cost, "delivery"));
                }
            }
        }
        // 2. 收集所有可选的取货任务
        for (BackpackData task : unloadedTasks) {
            TaskData taskData = getTaskData(task.getTaskId());
            if (taskData != null && taskData.getStart() != null) {
                double distance = calculateDistance(currentPosition, taskData.getStart());
                if (distance >= 0) {
                    double cost = calculateTaskCost(distance, "pickup", taskData.getPriority(), agvStatus);
                    taskOptions.add(new TaskOption(task, false, distance, cost, "pickup"));
                }
            }
        }
        // 3. 如果电量不足,添加充电选项
        if (agvStatus.needsCharging()) {
            String nearestCharging = findNearestChargingPosition(currentPosition);
            if (nearestCharging != null && !nearestCharging.equals(currentPosition)) {
                double distance = calculateDistance(currentPosition, nearestCharging);
                if (distance >= 0) {
                    double cost = calculateChargingCost(distance, agvStatus.getVol(), agvStatus.getLowVol());
                    taskOptions.add(new TaskOption(null, false, distance, cost, "charging"));
                }
            }
        }
        // 4. 选择成本最低的任务
        if (taskOptions.isEmpty()) {
            return null;
        }
        // 按成本排序,选择最优选项
        taskOptions.sort(Comparator.comparingDouble(option -> option.cost));
        TaskOption bestOption = taskOptions.get(0);
        // 输出决策日志
        System.out.println("AGV " + agvStatus.getAgvId() + " 任务决策:");
        for (int i = 0; i < Math.min(3, taskOptions.size()); i++) {
            TaskOption option = taskOptions.get(i);
            System.out.println("  " + (i == 0 ? "[选中]" : "      ") +
                    " 类型: " + option.taskType +
                    ", 距离: " + String.format("%.1f", option.distance) +
                    ", 成本: " + String.format("%.2f", option.cost));
        }
        // 创建执行任务
        if ("charging".equals(bestOption.taskType)) {
            String nearestCharging = findNearestChargingPosition(currentPosition);
            return createChargingTask(agvStatus, nearestCharging);
        } else {
            return createExecutingTask(agvStatus, bestOption.backpackData, bestOption.isLoaded);
        }
    }
    /**
     * 计算任务成本
     *
     * @param distance     距离
     * @param taskType     任务类型
     * @param taskPriority 任务优先级
     * @param agvStatus    AGV状态
     * @return 任务成本
     */
    private double calculateTaskCost(double distance, String taskType, int taskPriority, AGVStatus agvStatus) {
        // 基础距离成本
        double distanceCost = distance;
        // 任务类型权重
        double taskTypeWeight;
        switch (taskType) {
            case "delivery":
                taskTypeWeight = 10.0; // 送货任务基础权重较低,优先执行
                break;
            case "pickup":
                taskTypeWeight = 15.0; // 取货任务基础权重适中
                break;
            default:
                taskTypeWeight = 20.0;
        }
        // 任务优先级影响(优先级越高,权重越低)
        double priorityWeight = Math.max(1.0, 6.0 - taskPriority);
        // 电量影响(电量低时偏向近距离任务)
        double batteryFactor = 1.0;
        if (agvStatus.needsCharging()) {
            batteryFactor = 1.0 + (100.0 - agvStatus.getVol()) / 100.0; // 电量越低,距离成本越高
        }
        // 综合成本计算
        double totalCost = (distanceCost * batteryFactor) + (taskTypeWeight * priorityWeight);
        return totalCost;
    }
    /**
     * 计算充电任务成本
     *
     * @param distance   到充电点的距离
     * @param currentVol 当前电量
     * @param lowVol     最低电量阈值
     * @return 充电任务成本
     */
    private double calculateChargingCost(double distance, int currentVol, int lowVol) {
        // 基础距离成本
        double distanceCost = distance;
        // 电量紧急程度(电量越低,充电成本越低,即优先级越高)
        double batteryUrgency;
        if (currentVol <= lowVol) {
            batteryUrgency = 5.0; // 必须充电,极高优先级
        } else {
            // 电量在lowVol到autoCharge之间,线性计算紧急程度
            batteryUrgency = 30.0 - (currentVol - lowVol) * 0.5; // 电量越低优先级越高
        }
        return distanceCost + batteryUrgency;
    }
    /**
     * 任务选项内部类
     */
    private static class TaskOption {
        BackpackData backpackData;
        boolean isLoaded;
        double distance;
        double cost;
        String taskType;
        public TaskOption(BackpackData backpackData, boolean isLoaded, double distance, double cost, String taskType) {
            this.backpackData = backpackData;
            this.isLoaded = isLoaded;
            this.distance = distance;
            this.cost = cost;
            this.taskType = taskType;
        }
    }
    /**
     * 获取任务数据
     *
     * @param taskId 任务ID
     * @return 任务数据
     */
    private TaskData getTaskData(String taskId) {
        return taskDataMap.get(taskId);
    }
    /**
     * 查找最近的充电位置
     *
     * @param currentPosition 当前位置
     * @return 最近的充电位置
     */
    private String findNearestChargingPosition(String currentPosition) {
        String nearest = null;
        double minDistance = Double.MAX_VALUE;
        for (String chargingPos : chargingPositions) {
            if (!chargingPos.equals(currentPosition)) {
                double distance = calculateDistance(currentPosition, chargingPos);
                if (distance >= 0 && distance < minDistance) {
                    minDistance = distance;
                    nearest = chargingPos;
                }
            }
        }
        return nearest;
    }
    /**
     * 创建执行任务
     *
     * @param agvStatus    AGV状态
     * @param backpackData 背篓数据
     * @param isLoaded     是否已装载
     * @return 执行任务
     */
    private ExecutingTask createExecutingTask(AGVStatus agvStatus, BackpackData backpackData, boolean isLoaded) {
        TaskData taskData = getTaskData(backpackData.getTaskId());
        if (taskData == null) {
            return null;
        }
        ExecutingTask task = new ExecutingTask();
        task.setAgvId(agvStatus.getAgvId());
        task.setTaskId(backpackData.getTaskId());
        task.setCurrentPosition(agvStatus.getPosition());
        task.setLoaded(isLoaded);
        task.setBackpackIndex(backpackData.getIndex());
        task.setPriority(taskData.getPriority());
        // 设置目标位置
        if (isLoaded) {
            task.setTargetPosition(taskData.getEnd());
            task.setTaskType("delivery");
        } else {
            task.setTargetPosition(taskData.getStart());
            task.setTaskType("pickup");
        }
        // 计算距离
        task.setDistanceToStart(calculateDistance(agvStatus.getPosition(), taskData.getStart()));
        task.setDistanceToEnd(calculateDistance(agvStatus.getPosition(), taskData.getEnd()));
        return task;
    }
    /**
     * 创建充电任务
     *
     * @param agvStatus        AGV状态
     * @param chargingPosition 充电位置
     * @return 充电任务
     */
    private ExecutingTask createChargingTask(AGVStatus agvStatus, String chargingPosition) {
        ExecutingTask task = new ExecutingTask();
        task.setAgvId(agvStatus.getAgvId());
        task.setTaskId("CHARGING_" + agvStatus.getAgvId());
        task.setCurrentPosition(agvStatus.getPosition());
        task.setTargetPosition(chargingPosition);
        task.setTaskType("charging");
        task.setLoaded(false);
        task.setBackpackIndex(-1);
        task.setPriority(1); // 充电任务优先级较高
        return task;
    }
    /**
     * 计算两个位置之间的距离
     *
     * @param pos1 位置1
     * @param pos2 位置2
     * @return 距离值
     */
    private double calculateDistance(String pos1, String pos2) {
        if (pos1 == null || pos2 == null) {
            return -1;
        }
        int[] coord1 = JsonUtils.getCoordinate(pos1, pathMapping);
        int[] coord2 = JsonUtils.getCoordinate(pos2, pathMapping);
        if (coord1 == null || coord2 == null) {
            return -1;
        }
        return JsonUtils.calculateManhattanDistance(coord1, coord2);
    }
    /**
     * 构建坐标到路径编号的映射
     */
    private void buildCoordToCodeMapping() {
        for (Map.Entry<String, Map<String, Integer>> entry : pathMapping.entrySet()) {
            String code = entry.getKey();
            Map<String, Integer> coordMap = entry.getValue();
            if (coordMap.containsKey("x") && coordMap.containsKey("y")) {
                String coordKey = coordMap.get("x") + "," + coordMap.get("y");
                coordToCode.put(coordKey, code);
            }
        }
    }
    /**
     * 预分类位置类型
     */
    private void classifyPositions() {
        pickupPositions = new ArrayList<>();
        chargingPositions = new ArrayList<>();
        deliveryPositions = new ArrayList<>();
        standbyPositions = new ArrayList<>();
        for (String position : pathMapping.keySet()) {
            if (position.startsWith("1")) {
                pickupPositions.add(position);
            } else if (position.startsWith("2")) {
                chargingPositions.add(position);
            } else if (position.startsWith("3")) {
                deliveryPositions.add(position);
            } else if (position.startsWith("4")) {
                standbyPositions.add(position);
            }
        }
    }
    // Getter方法
    public List<String> getPickupPositions() {
        return pickupPositions;
    }
    public List<String> getChargingPositions() {
        return chargingPositions;
    }
    public List<String> getDeliveryPositions() {
        return deliveryPositions;
    }
    public List<String> getStandbyPositions() {
        return standbyPositions;
    }
}
algo-zkd/src/main/java/com/algo/service/PathPlanner.java
New file
@@ -0,0 +1,64 @@
package com.algo.service;
import com.algo.model.PlannedPath;
import java.util.List;
import java.util.Map;
/**
 * 路径规划器接口
 * 定义AGV路径规划方法
 */
public interface PathPlanner {
    /**
     * 规划单个AGV的路径
     *
     * @param startCode   起始路径点ID
     * @param endCode     目标路径点ID
     * @param constraints 约束条件列表 [x, y, radius]
     * @return 规划的路径,失败返回null
     */
    PlannedPath planPath(String startCode, String endCode, List<double[]> constraints);
    /**
     * 规划单个AGV的路径(无约束条件)
     *
     * @param startCode 起始路径点ID
     * @param endCode   目标路径点ID
     * @return 规划的路径,失败返回null
     */
    PlannedPath planPath(String startCode, String endCode);
    /**
     * 验证路径点是否有效
     *
     * @param pathCode 路径点ID
     * @return true如果路径点有效
     */
    boolean isValidPathPoint(String pathCode);
    /**
     * 计算两个路径点之间的距离
     *
     * @param startCode 起始路径点ID
     * @param endCode   目标路径点ID
     * @return 距离值,无效路径点返回-1
     */
    double calculateDistance(String startCode, String endCode);
    /**
     * 获取路径映射信息
     *
     * @return 路径映射表
     */
    Map<String, Map<String, Integer>> getPathMapping();
    /**
     * 获取邻接点列表
     *
     * @param pathCode 路径点ID
     * @return 邻接点列表及其方向
     */
    List<Map<String, String>> getNeighbors(String pathCode);
}
algo-zkd/src/main/java/com/algo/service/PathPlanningService.java
New file
@@ -0,0 +1,828 @@
package com.algo.service;
import com.algo.model.*;
import com.algo.util.JsonUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.*;
/**
 * 路径规划服务
 */
public class PathPlanningService {
    /**
     * 路径映射表
     */
    private Map<String, Map<String, Integer>> pathMapping;
    /**
     * 环境配置
     */
    private Map<String, Object> environmentConfig;
    /**
     * 执行中任务提取
     */
    private ExecutingTaskExtractor taskExtractor;
    /**
     * 路径规划
     */
    private PathPlanner pathPlanner;
    /**
     * 碰撞检测
     */
    private CollisionDetector collisionDetector;
    /**
     * 碰撞解决
     */
    private CollisionResolver collisionResolver;
    /**
     * 剩余路径处理
     */
    private RemainingPathProcessor remainingPathProcessor;
    /**
     * 线程池大小
     */
    private final int threadPoolSize;
    /**
     * 线程池
     */
    private final ExecutorService executorService;
    /**
     * CTU批处理大小
     */
    private final int batchSize = 10;
    /**
     * 构造函数
     *
     * @param pathMapping       路径映射表
     * @param environmentConfig 环境配置
     * @param taskDataList      任务数据列表
     */
    public PathPlanningService(Map<String, Map<String, Integer>> pathMapping,
                               Map<String, Object> environmentConfig,
                               List<TaskData> taskDataList) {
        this.pathMapping = pathMapping;
        this.environmentConfig = environmentConfig;
        this.threadPoolSize = Math.max(4, Runtime.getRuntime().availableProcessors());
        this.executorService = Executors.newFixedThreadPool(threadPoolSize);
        // 初始化
        initializeComponents(taskDataList);
        System.out.println("路径规划服务初始化完成(线程池大小: " + threadPoolSize + ")");
    }
    /**
     * 初始化各个组件
     *
     * @param taskDataList 任务数据列表
     */
    private void initializeComponents(List<TaskData> taskDataList) {
        // 初始化任务提取器
        this.taskExtractor = new ExecutingTaskExtractor(pathMapping, taskDataList);
        // 初始化路径规划器
        this.pathPlanner = new AStarPathPlanner(pathMapping);
        // 初始化碰撞检测器
        this.collisionDetector = new CollisionDetector(pathMapping);
        // 初始化碰撞解决器
        this.collisionResolver = new CollisionResolver(collisionDetector);
        // 初始化剩余路径处理器
        this.remainingPathProcessor = new RemainingPathProcessor(pathMapping);
    }
    /**
     * 为所有CTU规划路径
     *
     * @param agvStatusList  CTU状态列表
     * @param includeIdleAgv 是否包含空闲CTU
     * @param constraints    路径约束条件
     * @return 路径规划结果
     */
    public PathPlanningResult planAllAgvPaths(List<AGVStatus> agvStatusList,
                                              boolean includeIdleAgv,
                                              List<double[]> constraints) {
        long startTime = System.currentTimeMillis();
        System.out.println("开始为 " + agvStatusList.size() + " 个CTU规划");
        // 1. 构建现有剩余路径的时空占用表
        System.out.println("步骤1: 构建时空占用表");
        Map<String, String> spaceTimeOccupancyMap = remainingPathProcessor.buildSpaceTimeOccupancyMap(agvStatusList);
        System.out.println("时空占用表构建完成,包含 " + spaceTimeOccupancyMap.size() + " 个占用点");
        // 2. 分类处理CTU:有剩余路径的和需要新路径的
        List<AGVStatus> ctuWithRemainingPaths = new ArrayList<>();
        List<AGVStatus> ctuNeedingNewPaths = new ArrayList<>();
        for (AGVStatus agv : agvStatusList) {
            if (agv.hasRemainingPath()) {
                ctuWithRemainingPaths.add(agv);
            } else if (agv.canAcceptNewTask() || (includeIdleAgv && isAgvIdle(agv))) {
                ctuNeedingNewPaths.add(agv);
            }
        }
        System.out.println("CTU分类完成: 有剩余路径=" + ctuWithRemainingPaths.size() +
                ", 需要新路径=" + ctuNeedingNewPaths.size());
        // 3. 处理有剩余路径的CTU
        List<PlannedPath> plannedPaths = new ArrayList<>();
        List<ExecutingTask> executingTasks = new ArrayList<>();
        Map<String, String> plannedAgvIds = new HashMap<>();
        System.out.println("步骤2: 处理有剩余路径的CTU");
        for (AGVStatus agv : ctuWithRemainingPaths) {
            PlannedPath remainingPath = processRemainingPath(agv);
            if (remainingPath != null) {
                plannedPaths.add(remainingPath);
                plannedAgvIds.put(agv.getAgvId(), "REMAINING_PATH");
                // 创建对应的执行任务
                ExecutingTask task = createExecutingTaskFromRemainingPath(agv);
                if (task != null) {
                    executingTasks.add(task);
                }
                System.out.println("CTU " + agv.getAgvId() + " 剩余路径处理完成,路径长度: " +
                        remainingPath.getCodeList().size());
            }
        }
        // 4. 为需要新路径的CTU提取执行中任务
        System.out.println("步骤3: 提取需要新路径的CTU任务");
        List<ExecutingTask> newTasks = taskExtractor.extractExecutingTasks(ctuNeedingNewPaths);
        System.out.println("提取到 " + newTasks.size() + " 个新任务");
        // 5. 为新任务规划路径(考虑时空约束)
        System.out.println("步骤4: 为新任务规划路径");
        for (ExecutingTask task : newTasks) {
            String agvId = task.getAgvId();
            String currentPos = task.getCurrentPosition();
            String targetPos = task.getTargetPosition();
            // 避免重复规划
            if (plannedAgvIds.containsKey(agvId)) {
                System.out.println("跳过CTU " + agvId + ":已规划路径");
                continue;
            }
            // 验证位置信息
            if (currentPos == null || targetPos == null) {
                System.out.println("CTU " + agvId + " 位置信息无效,跳过");
                continue;
            }
            // 获取CTU状态信息
            AGVStatus agvStatus = findAgvStatus(agvId, ctuNeedingNewPaths);
            if (agvStatus == null) {
                System.out.println("CTU " + agvId + " 状态信息未找到,跳过");
                continue;
            }
            // 规划时空安全的路径
            PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
                    currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
            );
            if (plannedPath != null) {
                // 设置CTU信息
                plannedPath.setAgvId(agvId);
                plannedPath.setSegId(generateSegId(task.getTaskId(), agvId, task.getTaskType()));
                // 增强路径代码信息
                enhancePathCodes(plannedPath, task);
                plannedPaths.add(plannedPath);
                plannedAgvIds.put(agvId, targetPos);
                executingTasks.add(task);
                // 更新时空占用表
                updateSpaceTimeOccupancyMap(plannedPath, spaceTimeOccupancyMap, agvStatus);
                System.out.println("CTU " + agvId + " 路径规划成功:" + currentPos + " -> " + targetPos +
                        ",路径长度: " + plannedPath.getCodeList().size());
            } else {
                System.out.println("CTU " + agvId + " 路径规划失败:" + currentPos + " -> " + targetPos);
            }
        }
        // 6. 处理空闲CTU
        if (includeIdleAgv) {
            System.out.println("步骤5: 处理空闲CTU");
            List<PlannedPath> idlePaths = planIdleAgvPathsWithConstraints(
                    ctuNeedingNewPaths, plannedAgvIds, constraints, spaceTimeOccupancyMap
            );
            plannedPaths.addAll(idlePaths);
            System.out.println("空闲CTU路径规划完成,数量: " + idlePaths.size());
        }
        // 7. 最终碰撞检测和解决(针对同时生成的新路径)
        System.out.println("步骤6: 最终碰撞检测");
        List<Conflict> conflicts = collisionDetector.detectConflicts(plannedPaths);
        if (!conflicts.isEmpty()) {
            System.out.println("发现 " + conflicts.size() + " 个碰撞,开始解决");
            plannedPaths = collisionResolver.resolveConflicts(plannedPaths, conflicts, executingTasks);
            // 重新检测冲突
            List<Conflict> remainingConflicts = collisionDetector.detectConflicts(plannedPaths);
            conflicts = remainingConflicts;
        }
        long endTime = System.currentTimeMillis();
        double totalTime = (endTime - startTime) / 1000.0;
        // 构建结果
        PathPlanningResult result = new PathPlanningResult(
                agvStatusList.size(),
                executingTasks.size(),
                plannedPaths.size(),
                totalTime,
                conflicts.size(),
                conflicts.isEmpty(),
                "A*_WITH_SPACETIME_CONSTRAINTS",
                plannedPaths,
                executingTasks
        );
        System.out.println("路径规划完成 - 总CTU: " + result.getTotalAgvs() +
                ", 执行任务: " + result.getExecutingTasksCount() +
                ", 规划路径: " + result.getPlannedPathsCount() +
                ", 耗时: " + result.getTotalPlanningTime() + "s" +
                ", 无冲突: " + result.isCollisionFree());
        return result;
    }
    /**
     * 处理CTU的剩余路径
     *
     * @param agv CTU状态
     * @return 处理后的路径
     */
    private PlannedPath processRemainingPath(AGVStatus agv) {
        if (!agv.hasRemainingPath()) {
            return null;
        }
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> remainingCodes = new ArrayList<>();
        // 从当前位置开始,获取剩余路径
        List<PathCode> originalCodes = remainingPath.getCodeList();
        for (int i = agv.getCurrentPathIndex(); i < originalCodes.size(); i++) {
            remainingCodes.add(originalCodes.get(i));
        }
        // 创建新的路径对象
        PlannedPath processedPath = new PlannedPath();
        processedPath.setAgvId(agv.getAgvId());
        processedPath.setCodeList(remainingCodes);
        processedPath.setSegId(agv.getAgvId() + "_REMAINING_" + System.currentTimeMillis());
        return processedPath;
    }
    /**
     * 从剩余路径创建执行任务
     *
     * @param agv CTU状态
     * @return 执行任务
     */
    private ExecutingTask createExecutingTaskFromRemainingPath(AGVStatus agv) {
        if (!agv.hasRemainingPath()) {
            return null;
        }
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> codeList = remainingPath.getCodeList();
        if (codeList.isEmpty() || agv.getCurrentPathIndex() >= codeList.size()) {
            return null;
        }
        // 获取当前位置和目标位置
        PathCode currentCode = codeList.get(agv.getCurrentPathIndex());
        PathCode targetCode = codeList.get(codeList.size() - 1);
        ExecutingTask task = new ExecutingTask();
        task.setAgvId(agv.getAgvId());
        task.setTaskId(remainingPath.getSegId());
        task.setCurrentPosition(currentCode.getCode());
        task.setTargetPosition(targetCode.getCode());
        task.setTaskType("remaining");
        task.setLoaded(false); // 假设值
        task.setBackpackIndex(0);
        task.setPriority(1);
        return task;
    }
    /**
     * 规划带时空约束的路径
     *
     * @param startPos     起始位置
     * @param endPos       目标位置
     * @param constraints  约束条件
     * @param occupancyMap 时空占用表
     * @param agvStatus    CTU状态
     * @return 规划的路径
     */
    private PlannedPath planPathWithSpaceTimeConstraints(String startPos, String endPos,
                                                         List<double[]> constraints,
                                                         Map<String, String> occupancyMap,
                                                         AGVStatus agvStatus) {
        // 首先尝试基本路径规划
        PlannedPath basicPath = pathPlanner.planPath(startPos, endPos, constraints);
        if (basicPath == null) {
            return null;
        }
        // 找到安全的起始时间
        long safeStartTime = remainingPathProcessor.findSafeStartTime(
                basicPath, occupancyMap, agvStatus.getPhysicalConfig()
        );
        // 设置路径的时间信息
        enhancePathWithTimeInfo(basicPath, safeStartTime, agvStatus.getPhysicalConfig());
        return basicPath;
    }
    /**
     * 路径的时间信息
     *
     * @param path      路径
     * @param startTime 起始时间
     * @param config    物理配置
     */
    private void enhancePathWithTimeInfo(PlannedPath path, long startTime, CTUPhysicalConfig config) {
        List<PathCode> codeList = path.getCodeList();
        if (codeList == null || codeList.isEmpty()) {
            return;
        }
        long currentTime = startTime;
        for (int i = 0; i < codeList.size(); i++) {
            PathCode pathCode = codeList.get(i);
            // 设置预计到达时间
            // 计算到下一个点的时间
            if (i < codeList.size() - 1) {
                double travelTime = config.getStandardPointDistance() / config.getNormalSpeed();
                currentTime += (long) (travelTime * 1000);
                // 如果有方向变化,增加转向时间
                PathCode nextCode = codeList.get(i + 1);
                if (!pathCode.getDirection().equals(nextCode.getDirection())) {
                    double turnTime = config.getTurnTime(pathCode.getDirection(), nextCode.getDirection());
                    currentTime += (long) (turnTime * 1000);
                }
            }
        }
    }
    /**
     * 更新时空占用表
     *
     * @param path         新路径
     * @param occupancyMap 占用表
     * @param agvStatus    CTU状态
     */
    private void updateSpaceTimeOccupancyMap(PlannedPath path, Map<String, String> occupancyMap,
                                             AGVStatus agvStatus) {
        // 将新路径添加到占用表中
        List<PathCode> codeList = path.getCodeList();
        CTUPhysicalConfig config = agvStatus.getPhysicalConfig();
        long currentTime = System.currentTimeMillis() / 1000; // 转换为秒
        for (PathCode pathCode : codeList) {
            int[] coord = JsonUtils.getCoordinate(pathCode.getCode(), pathMapping);
            if (coord != null) {
                String spaceTimeKey = coord[0] + "," + coord[1] + "," + currentTime;
                occupancyMap.put(spaceTimeKey, agvStatus.getAgvId());
                currentTime += 2; // 假设每个点停留2秒
            }
        }
    }
    /**
     * 查找CTU状态
     *
     * @param agvId   CTU编号
     * @param agvList CTU列表
     * @return CTU状态
     */
    private AGVStatus findAgvStatus(String agvId, List<AGVStatus> agvList) {
        for (AGVStatus agv : agvList) {
            if (agv.getAgvId().equals(agvId)) {
                return agv;
            }
        }
        return null;
    }
    /**
     * 规划带约束的空闲CTU路径
     *
     * @param agvStatusList CTU状态列表
     * @param plannedAgvIds 已规划CTU映射
     * @param constraints   约束条件
     * @param occupancyMap  时空占用表
     * @return 空闲CTU路径列表
     */
    private List<PlannedPath> planIdleAgvPathsWithConstraints(List<AGVStatus> agvStatusList,
                                                              Map<String, String> plannedAgvIds,
                                                              List<double[]> constraints,
                                                              Map<String, String> occupancyMap) {
        List<PlannedPath> idlePaths = new ArrayList<>();
        for (AGVStatus agvStatus : agvStatusList) {
            String agvId = agvStatus.getAgvId();
            // 跳过已规划的CTU
            if (plannedAgvIds.containsKey(agvId)) {
                continue;
            }
            // 检查CTU是否空闲
            if (isAgvIdle(agvStatus)) {
                String currentPos = agvStatus.getPosition();
                String targetPos = getIdleAgvTarget(agvId, currentPos);
                if (targetPos != null && !targetPos.equals(currentPos)) {
                    PlannedPath idlePath = planPathWithSpaceTimeConstraints(
                            currentPos, targetPos, constraints, occupancyMap, agvStatus
                    );
                    if (idlePath != null) {
                        idlePath.setAgvId(agvId);
                        idlePath.setSegId(generateSegId("IDLE", agvId, "idle"));
                        // 设置空闲路径的代码信息
                        enhanceIdlePathCodes(idlePath, agvStatus);
                        idlePaths.add(idlePath);
                        plannedAgvIds.put(agvId, targetPos);
                        // 更新占用表
                        updateSpaceTimeOccupancyMap(idlePath, occupancyMap, agvStatus);
                        System.out.println("空闲CTU " + agvId + " 路径规划成功:" + currentPos + " -> " + targetPos);
                    }
                }
            }
        }
        return idlePaths;
    }
    /**
     * 路径代码信息
     *
     * @param plannedPath 规划路径
     * @param task        执行任务
     */
    private void enhancePathCodes(PlannedPath plannedPath, ExecutingTask task) {
        List<PathCode> codeList = plannedPath.getCodeList();
        if (codeList == null || codeList.isEmpty()) {
            return;
        }
        String actionType = "2"; // 任务类型
        String taskId = task.getTaskId();
        int backpackLevel = task.getBackpackIndex();
        for (int i = 0; i < codeList.size(); i++) {
            PathCode pathCode = codeList.get(i);
            // 设置基本信息
            pathCode.setActionType(actionType);
            pathCode.setTaskId(taskId);
            pathCode.setLev(backpackLevel);
            // 设置目标点信息
            if (i == codeList.size() - 1) { // 最后一个点
                pathCode.setTargetPoint(true);
                if (task.isLoaded()) {
                    pathCode.setPosType("2"); // 放货
                } else {
                    pathCode.setPosType("1"); // 取货
                }
            } else {
                pathCode.setTargetPoint(false);
                pathCode.setPosType(null);
            }
        }
    }
    /**
     * 空闲路径代码信息
     *
     * @param plannedPath 规划路径
     * @param agvStatus   CTU状态
     */
    private void enhanceIdlePathCodes(PlannedPath plannedPath, AGVStatus agvStatus) {
        List<PathCode> codeList = plannedPath.getCodeList();
        if (codeList == null || codeList.isEmpty()) {
            return;
        }
        String actionType = agvStatus.needsCharging() ? "3" : "4"; // 充电或待机
        for (PathCode pathCode : codeList) {
            pathCode.setActionType(actionType);
            pathCode.setTaskId(null);
            pathCode.setPosType(null);
            pathCode.setLev(0);
            pathCode.setTargetPoint(false);
        }
    }
    /**
     * 判断CTU是否空闲
     *
     * @param agvStatus CTU状态
     * @return true如果CTU空闲
     */
    private boolean isAgvIdle(AGVStatus agvStatus) {
        if (!agvStatus.isAvailable()) {
            return false;
        }
        // 检查背篓是否有执行中的任务
        List<BackpackData> backpack = agvStatus.getBackpack();
        if (backpack != null) {
            for (BackpackData bp : backpack) {
                if (bp.getTaskId() != null && !bp.getTaskId().trim().isEmpty()) {
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * 获取空闲CTU的目标位置
     *
     * @param agvId           CTU编号
     * @param currentPosition 当前位置
     * @return 目标位置
     */
    private String getIdleAgvTarget(String agvId, String currentPosition) {
        // 根据CTU ID的哈希值选择位置
        int hashValue = Math.abs(agvId.hashCode() % 1000);
        // 优先选择充电位置
        List<String> chargingPositions = taskExtractor.getChargingPositions();
        if (!chargingPositions.isEmpty()) {
            for (String pos : chargingPositions) {
                if (!pos.equals(currentPosition)) {
                    return pos;
                }
            }
        }
        // 其次选择待机位置
        List<String> standbyPositions = taskExtractor.getStandbyPositions();
        if (!standbyPositions.isEmpty()) {
            return standbyPositions.get(hashValue % standbyPositions.size());
        }
        return null;
    }
    /**
     * 生成段落ID
     *
     * @param taskId   任务ID
     * @param agvId    CTU编号
     * @param taskType 任务类型
     * @return 段落ID
     */
    private String generateSegId(String taskId, String agvId, String taskType) {
        return taskId + "_" + agvId + "_" + taskType;
    }
    /**
     * 路径规划结果类
     */
    public static class PathPlanningResult {
        private int totalAgvs;
        private int executingTasksCount;
        private int plannedPathsCount;
        private double totalPlanningTime;
        private int conflictsDetected;
        private boolean collisionFree;
        private String algorithm;
        private List<PlannedPath> plannedPaths;
        private List<ExecutingTask> executingTasks;
        public PathPlanningResult(int totalAgvs, int executingTasksCount, int plannedPathsCount,
                                  double totalPlanningTime, int conflictsDetected, boolean collisionFree,
                                  String algorithm, List<PlannedPath> plannedPaths, List<ExecutingTask> executingTasks) {
            this.totalAgvs = totalAgvs;
            this.executingTasksCount = executingTasksCount;
            this.plannedPathsCount = plannedPathsCount;
            this.totalPlanningTime = totalPlanningTime;
            this.conflictsDetected = conflictsDetected;
            this.collisionFree = collisionFree;
            this.algorithm = algorithm;
            this.plannedPaths = plannedPaths;
            this.executingTasks = executingTasks;
        }
        // Getter方法
        public int getTotalAgvs() {
            return totalAgvs;
        }
        public int getExecutingTasksCount() {
            return executingTasksCount;
        }
        public int getPlannedPathsCount() {
            return plannedPathsCount;
        }
        public double getTotalPlanningTime() {
            return totalPlanningTime;
        }
        public int getConflictsDetected() {
            return conflictsDetected;
        }
        public boolean isCollisionFree() {
            return collisionFree;
        }
        public String getAlgorithm() {
            return algorithm;
        }
        public List<PlannedPath> getPlannedPaths() {
            return plannedPaths;
        }
        public List<ExecutingTask> getExecutingTasks() {
            return executingTasks;
        }
        @Override
        public String toString() {
            return "PathPlanningResult{" +
                    "totalAgvs=" + totalAgvs +
                    ", executingTasksCount=" + executingTasksCount +
                    ", plannedPathsCount=" + plannedPathsCount +
                    ", executingTasksCount=" + executingTasksCount +
                    ", totalPlanningTime=" + totalPlanningTime +
                    ", conflictsDetected=" + conflictsDetected +
                    ", collisionFree=" + executingTasksCount +
                    ", algorithm=" + algorithm +
                    ", plannedPaths=" + plannedPaths +
                    ", executingTasks='" + executingTasks + '\'' +
                    '}';
        }
    }
    /**
     * 并行规划任务路径
     */
    private List<PlannedPath> planTasksInParallel(List<ExecutingTask> tasks,
                                                  List<double[]> constraints,
                                                  Map<String, String> spaceTimeOccupancyMap,
                                                  List<AGVStatus> agvStatusList) {
        List<PlannedPath> allPaths = Collections.synchronizedList(new ArrayList<>());
        // 按优先级排序任务
        List<ExecutingTask> sortedTasks = new ArrayList<>(tasks);
        sortedTasks.sort((t1, t2) -> Integer.compare(t2.getPriority(), t1.getPriority()));
        // 分批处理
        List<List<ExecutingTask>> batches = createBatches(sortedTasks, batchSize);
        for (int batchIndex = 0; batchIndex < batches.size(); batchIndex++) {
            List<ExecutingTask> batch = batches.get(batchIndex);
            System.out.println("处理批次 " + (batchIndex + 1) + "/" + batches.size() +
                    ", 任务数: " + batch.size());
            List<Future<PlannedPath>> futures = new ArrayList<>();
            for (ExecutingTask task : batch) {
                Future<PlannedPath> future = executorService.submit(() -> {
                    return planSingleTaskPath(task, constraints, spaceTimeOccupancyMap, agvStatusList);
                });
                futures.add(future);
            }
            for (Future<PlannedPath> future : futures) {
                try {
                    PlannedPath path = future.get(60, TimeUnit.SECONDS);
                    if (path != null) {
                        allPaths.add(path);
                        System.out.println("CTU " + path.getAgvId() + " 路径规划成功");
                    }
                } catch (TimeoutException e) {
                    System.out.println("路径规划超时,跳过");
                    future.cancel(true);
                } catch (Exception e) {
                    System.out.println("路径规划异常: " + e.getMessage());
                }
            }
        }
        return allPaths;
    }
    /**
     * 为单个任务规划路径
     */
    private PlannedPath planSingleTaskPath(ExecutingTask task,
                                           List<double[]> constraints,
                                           Map<String, String> spaceTimeOccupancyMap,
                                           List<AGVStatus> agvStatusList) {
        String agvId = task.getAgvId();
        String currentPos = task.getCurrentPosition();
        String targetPos = task.getTargetPosition();
        if (currentPos == null || targetPos == null) {
            return null;
        }
        // 获取CTU状态信息
        AGVStatus agvStatus = findAgvStatus(agvId, agvStatusList);
        if (agvStatus == null) {
            return null;
        }
        // 规划时空安全的路径
        PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
                currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
        );
        if (plannedPath != null) {
            // 设置CTU信息
            plannedPath.setAgvId(agvId);
            plannedPath.setSegId(generateSegId(task.getTaskId(), agvId, task.getTaskType()));
            enhancePathCodes(plannedPath, task);
        }
        return plannedPath;
    }
    /**
     * 创建批次
     */
    private List<List<ExecutingTask>> createBatches(List<ExecutingTask> tasks, int batchSize) {
        List<List<ExecutingTask>> batches = new ArrayList<>();
        for (int i = 0; i < tasks.size(); i += batchSize) {
            int end = Math.min(i + batchSize, tasks.size());
            batches.add(new ArrayList<>(tasks.subList(i, end)));
        }
        return batches;
    }
    /**
     * 关闭服务
     */
    public void shutdown() {
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
            try {
                if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
                    executorService.shutdownNow();
                }
            } catch (InterruptedException e) {
                executorService.shutdownNow();
                Thread.currentThread().interrupt();
            }
        }
    }
}
algo-zkd/src/main/java/com/algo/service/RemainingPathProcessor.java
New file
@@ -0,0 +1,416 @@
package com.algo.service;
import com.algo.model.AGVStatus;
import com.algo.model.CTUPhysicalConfig;
import com.algo.model.PathCode;
import com.algo.model.PlannedPath;
import com.algo.util.JsonUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * 剩余路径处理器
 * 负责处理CTU未完成路径的续接逻辑,确保新路径与剩余路径的平滑连接
 */
public class RemainingPathProcessor {
    /**
     * 路径映射表
     */
    private final Map<String, Map<String, Integer>> pathMapping;
    /**
     * 系统当前时间基准(毫秒)
     */
    private long systemBaseTime;
    public RemainingPathProcessor(Map<String, Map<String, Integer>> pathMapping) {
        this.pathMapping = pathMapping;
        this.systemBaseTime = System.currentTimeMillis();
    }
    /**
     * 处理所有CTU的剩余路径,构建时空占用表
     *
     * @param agvStatusList CTU状态列表
     * @return 时空占用表,key为"x,y,timeSlot",value为CTU编号
     */
    public Map<String, String> buildSpaceTimeOccupancyMap(List<AGVStatus> agvStatusList) {
        Map<String, String> occupancyMap = new HashMap<>();
        for (AGVStatus agv : agvStatusList) {
            if (agv.hasRemainingPath()) {
                processRemainingPathOccupancy(agv, occupancyMap);
            }
        }
        return occupancyMap;
    }
    /**
     * 处理单个CTU的剩余路径时空占用
     *
     * @param agv          CTU状态
     * @param occupancyMap 时空占用表
     */
    private void processRemainingPathOccupancy(AGVStatus agv, Map<String, String> occupancyMap) {
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> codeList = remainingPath.getCodeList();
        CTUPhysicalConfig config = agv.getPhysicalConfig();
        // 从当前位置开始计算时空占用
        long currentTime = agv.getNextPointArrivalTime();
        int startIndex = agv.getCurrentPathIndex();
        for (int i = startIndex; i < codeList.size(); i++) {
            PathCode pathCode = codeList.get(i);
            String position = pathCode.getCode();
            // 获取位置坐标
            int[] coord = JsonUtils.getCoordinate(position, pathMapping);
            if (coord == null) continue;
            // 计算在此位置的停留时间
            double stayDuration = calculateStayDuration(agv, i, codeList, config);
            // 将停留时间转换为时间片
            long startTimeSlot = currentTime / 1000; // 转换为秒
            long endTimeSlot = (long) (currentTime / 1000 + stayDuration);
            // 占用时空
            for (long timeSlot = startTimeSlot; timeSlot <= endTimeSlot; timeSlot++) {
                String spaceTimeKey = coord[0] + "," + coord[1] + "," + timeSlot;
                occupancyMap.put(spaceTimeKey, agv.getAgvId());
                // 考虑CTU的物理尺寸,占用周围的格子
                occupyAdjacentSpaces(coord, timeSlot, agv.getAgvId(), occupancyMap, config);
            }
            // 更新到下一个位置的时间
            currentTime += (long) (stayDuration * 1000);
            if (i < codeList.size() - 1) {
                PathCode nextPathCode = codeList.get(i + 1);
                double travelTime = calculateTravelTime(pathCode, nextPathCode, config);
                currentTime += (long) (travelTime * 1000);
            }
        }
    }
    /**
     * 占用相邻空间(考虑CTU物理尺寸)
     *
     * @param centerCoord  中心坐标
     * @param timeSlot     时间片
     * @param agvId        CTU编号
     * @param occupancyMap 占用表
     * @param config       物理配置
     */
    private void occupyAdjacentSpaces(int[] centerCoord, long timeSlot, String agvId,
                                      Map<String, String> occupancyMap, CTUPhysicalConfig config) {
        // 根据CTU的长度和宽度计算需要占用的格子数
        int lengthGrids = (int) Math.ceil(config.getCtuLength() / config.getStandardPointDistance());
        int widthGrids = (int) Math.ceil(config.getCtuWidth() / config.getStandardPointDistance());
        // 占用周围的格子
        for (int dx = -lengthGrids / 2; dx <= lengthGrids / 2; dx++) {
            for (int dy = -widthGrids / 2; dy <= widthGrids / 2; dy++) {
                if (dx == 0 && dy == 0) continue; // 中心点已经占用
                int adjX = centerCoord[0] + dx;
                int adjY = centerCoord[1] + dy;
                String spaceTimeKey = adjX + "," + adjY + "," + timeSlot;
                // 只有当该位置未被占用时才占用
                if (!occupancyMap.containsKey(spaceTimeKey)) {
                    occupancyMap.put(spaceTimeKey, agvId);
                }
            }
        }
    }
    /**
     * 计算CTU在指定路径点的停留时间
     *
     * @param agv       CTU状态
     * @param pathIndex 路径点索引
     * @param codeList  完整路径代码列表
     * @param config    物理配置
     * @return 停留时间(秒)
     */
    private double calculateStayDuration(AGVStatus agv, int pathIndex,
                                         List<PathCode> codeList, CTUPhysicalConfig config) {
        PathCode currentCode = codeList.get(pathIndex);
        // 基础停留时间
        double stayTime = 0.5; // 默认0.5秒
        // 如果需要转向,增加转向时间
        if (pathIndex < codeList.size() - 1) {
            PathCode nextCode = codeList.get(pathIndex + 1);
            String currentDirection = currentCode.getDirection();
            String nextDirection = nextCode.getDirection();
            if (!currentDirection.equals(nextDirection)) {
                stayTime += config.getTurnTime(currentDirection, nextDirection);
            }
        }
        // 如果是动作点(取货、放货等),增加操作时间
        if (currentCode.getActionType() != null) {
            switch (currentCode.getActionType()) {
                case "1": // 取货
                    stayTime += 3.0; // 取货需要3秒
                    break;
                case "2": // 放货
                    stayTime += 2.0; // 放货需要2秒
                    break;
                case "3": // 充电
                    stayTime += 10.0; // 充电停靠需要10秒
                    break;
                default:
                    stayTime += 1.0; // 其他动作需要1秒
                    break;
            }
        }
        return stayTime;
    }
    /**
     * 计算两个路径点之间的移动时间
     *
     * @param fromCode 起始路径点
     * @param toCode   目标路径点
     * @param config   物理配置
     * @return 移动时间(秒)
     */
    private double calculateTravelTime(PathCode fromCode, PathCode toCode, CTUPhysicalConfig config) {
        // 获取坐标
        int[] fromCoord = JsonUtils.getCoordinate(fromCode.getCode(), pathMapping);
        int[] toCoord = JsonUtils.getCoordinate(toCode.getCode(), pathMapping);
        if (fromCoord == null || toCoord == null) {
            return config.getStandardPointDistance() / config.getNormalSpeed();
        }
        // 计算距离
        double distance = Math.sqrt(Math.pow(toCoord[0] - fromCoord[0], 2) +
                Math.pow(toCoord[1] - fromCoord[1], 2)) *
                config.getStandardPointDistance();
        // 计算移动时间
        return config.getTravelTime(distance, config.getNormalSpeed());
    }
    /**
     * 为新路径查找合适的起始时间,避免与现有路径冲突
     *
     * @param newPath      新路径
     * @param occupancyMap 现有时空占用表
     * @param config       物理配置
     * @return 建议的起始时间(毫秒)
     */
    public long findSafeStartTime(PlannedPath newPath, Map<String, String> occupancyMap,
                                  CTUPhysicalConfig config) {
        if (newPath.getCodeList() == null || newPath.getCodeList().isEmpty()) {
            return systemBaseTime;
        }
        // 从当前时间开始,逐步向后搜索安全的起始时间
        long currentTime = systemBaseTime;
        long timeStep = 1000; // 1秒步长
        long maxSearchTime = currentTime + 300000; // 最多搜索5分钟
        while (currentTime < maxSearchTime) {
            if (isPathTimeSafe(newPath, currentTime, occupancyMap, config)) {
                return currentTime;
            }
            currentTime += timeStep;
        }
        // 如果找不到安全时间,返回较远的未来时间
        return maxSearchTime;
    }
    /**
     * 检查指定时间开始的路径是否安全
     *
     * @param path         路径
     * @param startTime    起始时间
     * @param occupancyMap 占用表
     * @param config       物理配置
     * @return true如果安全
     */
    private boolean isPathTimeSafe(PlannedPath path, long startTime,
                                   Map<String, String> occupancyMap, CTUPhysicalConfig config) {
        List<PathCode> codeList = path.getCodeList();
        long currentTime = startTime;
        for (int i = 0; i < codeList.size(); i++) {
            PathCode pathCode = codeList.get(i);
            int[] coord = JsonUtils.getCoordinate(pathCode.getCode(), pathMapping);
            if (coord == null) continue;
            // 计算在此位置的停留时间
            double stayDuration = 2.0; // 简化计算,使用固定停留时间
            long startTimeSlot = currentTime / 1000;
            long endTimeSlot = (long) (currentTime / 1000 + stayDuration);
            // 检查时空冲突
            for (long timeSlot = startTimeSlot; timeSlot <= endTimeSlot; timeSlot++) {
                String spaceTimeKey = coord[0] + "," + coord[1] + "," + timeSlot;
                if (occupancyMap.containsKey(spaceTimeKey)) {
                    return false; // 发现冲突
                }
                // 检查安全距离内的冲突
                if (!isSafeFromOtherCTUs(coord, timeSlot, occupancyMap, config)) {
                    return false;
                }
            }
            // 更新到下一个位置的时间
            currentTime += (long) (stayDuration * 1000);
            if (i < codeList.size() - 1) {
                currentTime += (long) (config.getStandardPointDistance() / config.getNormalSpeed() * 1000);
            }
        }
        return true;
    }
    /**
     * 检查指定位置和时间是否与其他CTU保持安全距离
     *
     * @param coord        位置坐标
     * @param timeSlot     时间片
     * @param occupancyMap 占用表
     * @param config       物理配置
     * @return true如果安全
     */
    private boolean isSafeFromOtherCTUs(int[] coord, long timeSlot,
                                        Map<String, String> occupancyMap, CTUPhysicalConfig config) {
        int safetyRadius = (int) Math.ceil(config.getMinSafetyDistance() / config.getStandardPointDistance());
        // 检查安全半径内是否有其他CTU
        for (int dx = -safetyRadius; dx <= safetyRadius; dx++) {
            for (int dy = -safetyRadius; dy <= safetyRadius; dy++) {
                if (dx == 0 && dy == 0) continue;
                String checkKey = (coord[0] + dx) + "," + (coord[1] + dy) + "," + timeSlot;
                if (occupancyMap.containsKey(checkKey)) {
                    // 计算实际距离
                    double distance = Math.sqrt(dx * dx + dy * dy) * config.getStandardPointDistance();
                    if (distance < config.getMinSafetyDistance()) {
                        return false; // 距离太近
                    }
                }
            }
        }
        return true;
    }
    /**
     * 连接剩余路径和新路径
     *
     * @param agv     CTU状态
     * @param newPath 新规划的路径
     * @return 连接后的完整路径
     */
    public PlannedPath connectRemainingAndNewPath(AGVStatus agv, PlannedPath newPath) {
        if (!agv.hasRemainingPath()) {
            // 没有剩余路径,直接返回新路径
            return newPath;
        }
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> remainingCodes = new ArrayList<>();
        // 添加剩余路径(从当前位置开始)
        List<PathCode> originalCodes = remainingPath.getCodeList();
        for (int i = agv.getCurrentPathIndex(); i < originalCodes.size(); i++) {
            remainingCodes.add(originalCodes.get(i));
        }
        // 连接新路径
        if (newPath != null && newPath.getCodeList() != null) {
            // 检查连接点是否一致,避免重复
            if (!remainingCodes.isEmpty() && !newPath.getCodeList().isEmpty()) {
                PathCode lastRemaining = remainingCodes.get(remainingCodes.size() - 1);
                PathCode firstNew = newPath.getCodeList().get(0);
                if (lastRemaining.getCode().equals(firstNew.getCode())) {
                    // 跳过重复的连接点
                    for (int i = 1; i < newPath.getCodeList().size(); i++) {
                        remainingCodes.add(newPath.getCodeList().get(i));
                    }
                } else {
                    // 直接连接
                    remainingCodes.addAll(newPath.getCodeList());
                }
            } else {
                remainingCodes.addAll(newPath.getCodeList());
            }
        }
        // 创建连接后的路径
        PlannedPath connectedPath = new PlannedPath();
        connectedPath.setAgvId(agv.getAgvId());
        connectedPath.setCodeList(remainingCodes);
        // 生成新的段落ID
        String segId = agv.getAgvId() + "_CONNECTED_" + System.currentTimeMillis();
        if (newPath != null && newPath.getSegId() != null) {
            segId = newPath.getSegId() + "_EXTENDED";
        }
        connectedPath.setSegId(segId);
        return connectedPath;
    }
    /**
     * 更新系统基准时间
     *
     * @param baseTime 新的基准时间
     */
    public void updateSystemBaseTime(long baseTime) {
        this.systemBaseTime = baseTime;
    }
    /**
     * 获取CTU预计完成剩余路径的时间
     *
     * @param agv CTU状态
     * @return 预计完成时间(毫秒)
     */
    public long getEstimatedCompletionTime(AGVStatus agv) {
        if (!agv.hasRemainingPath()) {
            return System.currentTimeMillis();
        }
        PlannedPath remainingPath = agv.getRemainingPath();
        List<PathCode> codeList = remainingPath.getCodeList();
        CTUPhysicalConfig config = agv.getPhysicalConfig();
        long currentTime = agv.getNextPointArrivalTime();
        // 计算完成剩余路径所需的时间
        for (int i = agv.getCurrentPathIndex(); i < codeList.size(); i++) {
            double stayDuration = calculateStayDuration(agv, i, codeList, config);
            currentTime += (long) (stayDuration * 1000);
            if (i < codeList.size() - 1) {
                PathCode currentCode = codeList.get(i);
                PathCode nextCode = codeList.get(i + 1);
                double travelTime = calculateTravelTime(currentCode, nextCode, config);
                currentTime += (long) (travelTime * 1000);
            }
        }
        return currentTime;
    }
}
algo-zkd/src/main/java/com/algo/service/TaskAllocationService.java
New file
@@ -0,0 +1,292 @@
package com.algo.service;
import com.algo.model.AGVStatus;
import com.algo.model.BackpackData;
import com.algo.model.TaskAssignment;
import com.algo.model.TaskData;
import com.algo.util.JsonUtils;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 任务分配服务类
 */
public class TaskAllocationService {
    private Map<String, Map<String, Integer>> pathMapping;
    private Map<String, Object> environmentConfig;
    /**
     * 构造函数
     *
     * @param pathMapping       路径映射信息
     * @param environmentConfig 环境配置信息
     */
    public TaskAllocationService(Map<String, Map<String, Integer>> pathMapping,
                                 Map<String, Object> environmentConfig) {
        this.pathMapping = pathMapping;
        this.environmentConfig = environmentConfig;
    }
    /**
     * 执行任务分配
     * 根据AGV状态和任务列表进行智能分配
     *
     * @param agvStatusList AGV状态列表
     * @param taskList      任务列表
     * @return 任务分配结果列表
     */
    public List<TaskAssignment> allocateTasks(List<AGVStatus> agvStatusList, List<TaskData> taskList) {
        System.out.println("开始任务分配,AGV数量: " + agvStatusList.size() + ", 任务数量: " + taskList.size());
        // 输出环境信息
        System.out.println("环境信息:宽度=" + environmentConfig.get("width") +
                ", 高度=" + environmentConfig.get("height") +
                ", 工作站数量=" + environmentConfig.get("stationCount"));
        List<TaskAssignment> assignments = new ArrayList<>();
        // 1. 过滤可用的AGV
        List<AGVStatus> availableAgvs = agvStatusList.stream()
                .filter(agv -> agv.isAvailable() && agv.getAvailableBackpackCount() > 0)
                .collect(Collectors.toList());
        System.out.println("可用AGV数量: " + availableAgvs.size());
        if (availableAgvs.isEmpty()) {
            System.out.println("没有可用的AGV,任务分配结束");
            return assignments;
        }
        // 2. 过滤需要分配的任务(排除已指定AGV的任务)
        List<TaskData> unassignedTasks = taskList.stream()
                .filter(task -> !task.isAssignedToAgv() && task.isTransportTask())
                .collect(Collectors.toList());
        System.out.println("需要分配的运输任务数量: " + unassignedTasks.size());
        // 3. 按优先级排序任务
        unassignedTasks.sort((t1, t2) -> Integer.compare(t2.getPriority(), t1.getPriority()));
        // 4. 使用智能分配策略(考虑工作站信息)
        assignments.addAll(performIntelligentAllocation(availableAgvs, unassignedTasks));
        System.out.println("任务分配完成,生成分配结果数量: " + assignments.size());
        return assignments;
    }
    /**
     * 执行智能分配策略
     * 考虑AGV位置、任务位置、背篓容量、工作站容量等因素
     *
     * @param availableAgvs 可用AGV列表
     * @param tasks         待分配任务列表
     * @return 分配结果列表
     */
    private List<TaskAssignment> performIntelligentAllocation(List<AGVStatus> availableAgvs, List<TaskData> tasks) {
        List<TaskAssignment> assignments = new ArrayList<>();
        // 为每个AGV维护一个工作负载记录
        Map<String, Integer> agvWorkLoad = new HashMap<>();
        Map<String, Integer> agvUsedBackpacks = new HashMap<>();
        for (AGVStatus agv : availableAgvs) {
            agvWorkLoad.put(agv.getAgvId(), 0);
            agvUsedBackpacks.put(agv.getAgvId(), 0);
        }
        // 按地理位置分组任务(考虑工作站信息)
        Map<String, List<TaskData>> tasksByStartLocation = groupTasksByStartLocation(tasks);
        // 为每个位置分组分配AGV
        for (Map.Entry<String, List<TaskData>> entry : tasksByStartLocation.entrySet()) {
            String startLocation = entry.getKey();
            List<TaskData> locationTasks = entry.getValue();
            System.out.println("处理起点 " + startLocation + " 的任务,数量: " + locationTasks.size());
            // 检查是否为工作站
            if (JsonUtils.isStation(startLocation, environmentConfig)) {
                Map<String, Object> stationInfo = JsonUtils.getStationInfo(startLocation, environmentConfig);
                if (stationInfo != null) {
                    Integer capacity = (Integer) stationInfo.get("capacity");
                    System.out.println("工作站 " + startLocation + " 容量: " + capacity);
                    // 限制同时分配给该工作站的任务数量(不超过容量)
                    locationTasks = locationTasks.subList(0, Math.min(locationTasks.size(), capacity));
                }
            }
            // 找到距离该位置最近的AGV
            AGVStatus bestAgv = findNearestAvailableAgv(availableAgvs, startLocation, agvUsedBackpacks);
            if (bestAgv != null) {
                // 为该AGV分配尽可能多的任务(考虑背篓容量)
                int availableBackpacks = bestAgv.getAvailableBackpackCount() - agvUsedBackpacks.get(bestAgv.getAgvId());
                int tasksToAssign = Math.min(locationTasks.size(), availableBackpacks);
                for (int i = 0; i < tasksToAssign; i++) {
                    TaskData task = locationTasks.get(i);
                    // 查找可用的背篓位置
                    int backpackIndex = findAvailableBackpackIndex(bestAgv, agvUsedBackpacks.get(bestAgv.getAgvId()));
                    if (backpackIndex != -1) {
                        // 创建取货任务分配
                        TaskAssignment takeAssignment = new TaskAssignment(
                                task.getTaskId(),
                                bestAgv.getAgvId(),
                                TaskAssignment.generateSegId(task.getTaskId(), bestAgv.getAgvId(), 1),
                                String.valueOf(backpackIndex),
                                TaskAssignment.TYPE_TAKE
                        );
                        assignments.add(takeAssignment);
                        // 创建放货任务分配
                        TaskAssignment putAssignment = new TaskAssignment(
                                task.getTaskId(),
                                bestAgv.getAgvId(),
                                TaskAssignment.generateSegId(task.getTaskId(), bestAgv.getAgvId(), 2),
                                String.valueOf(backpackIndex),
                                TaskAssignment.TYPE_PUT
                        );
                        assignments.add(putAssignment);
                        // 更新AGV工作负载
                        agvWorkLoad.put(bestAgv.getAgvId(), agvWorkLoad.get(bestAgv.getAgvId()) + 1);
                        agvUsedBackpacks.put(bestAgv.getAgvId(), agvUsedBackpacks.get(bestAgv.getAgvId()) + 1);
                        System.out.println("分配任务 " + task.getTaskId() + " 给AGV " + bestAgv.getAgvId() +
                                " 背篓 " + backpackIndex + " (起点: " + startLocation + ")");
                    } else {
                        System.out.println("AGV " + bestAgv.getAgvId() + " 没有可用的背篓位置");
                        break;
                    }
                }
            } else {
                System.out.println("未找到可用的AGV处理起点 " + startLocation + " 的任务");
            }
        }
        return assignments;
    }
    /**
     * 按起点位置分组任务
     *
     * @param tasks 任务列表
     * @return 按起点分组的任务Map
     */
    private Map<String, List<TaskData>> groupTasksByStartLocation(List<TaskData> tasks) {
        Map<String, List<TaskData>> grouped = new HashMap<>();
        for (TaskData task : tasks) {
            String startLocation = task.getStart();
            grouped.computeIfAbsent(startLocation, k -> new ArrayList<>()).add(task);
        }
        return grouped;
    }
    /**
     * 查找距离指定位置最近的可用AGV
     *
     * @param availableAgvs  可用AGV列表
     * @param targetLocation 目标位置
     * @param usedBackpacks  已使用背篓数量统计
     * @return 最近的可用AGV
     */
    private AGVStatus findNearestAvailableAgv(List<AGVStatus> availableAgvs, String targetLocation, Map<String, Integer> usedBackpacks) {
        AGVStatus bestAgv = null;
        double minDistance = Double.MAX_VALUE;
        int[] targetCoord = JsonUtils.getCoordinate(targetLocation, pathMapping);
        if (targetCoord == null) {
            System.out.println("无法获取目标位置 " + targetLocation + " 的坐标");
            return availableAgvs.isEmpty() ? null : availableAgvs.get(0);
        }
        for (AGVStatus agv : availableAgvs) {
            // 检查AGV是否还有可用背篓
            int availableBackpacks = agv.getAvailableBackpackCount() - usedBackpacks.get(agv.getAgvId());
            if (availableBackpacks <= 0) {
                continue;
            }
            int[] agvCoord = JsonUtils.getCoordinate(agv.getPosition(), pathMapping);
            if (agvCoord != null) {
                double distance = JsonUtils.calculateManhattanDistance(agvCoord, targetCoord);
                // 考虑AGV的工作负载(距离越近且负载越轻越好)
                double adjustedDistance = distance + usedBackpacks.get(agv.getAgvId()) * 2.0;
                if (adjustedDistance < minDistance) {
                    minDistance = adjustedDistance;
                    bestAgv = agv;
                }
            }
        }
        return bestAgv;
    }
    /**
     * 查找AGV的可用背篓位置
     *
     * @param agv       AGV状态
     * @param usedCount 已使用的背篓数量
     * @return 可用背篓索引,如果没有可用则返回-1
     */
    private int findAvailableBackpackIndex(AGVStatus agv, int usedCount) {
        if (agv.getBackpack() == null || agv.getBackpack().isEmpty()) {
            return usedCount == 0 ? 0 : -1;
        }
        int availableCount = 0;
        for (BackpackData backpack : agv.getBackpack()) {
            if (backpack.isAvailable()) {
                if (availableCount >= usedCount) {
                    return backpack.getIndex();
                }
                availableCount++;
            }
        }
        return -1;
    }
    /**
     * 验证任务分配结果
     *
     * @param assignments 分配结果列表
     * @return 验证是否通过
     */
    public boolean validateAssignments(List<TaskAssignment> assignments) {
        Map<String, Set<String>> agvBackpackUsage = new HashMap<>();
        for (TaskAssignment assignment : assignments) {
            if (!assignment.isValid()) {
                System.out.println("发现无效的任务分配: " + assignment);
                return false;
            }
            String agvId = assignment.getAgvId();
            String backpackId = assignment.getLev();
            agvBackpackUsage.computeIfAbsent(agvId, k -> new HashSet<>()).add(backpackId);
        }
        // 检查背篓使用冲突
        for (Map.Entry<String, Set<String>> entry : agvBackpackUsage.entrySet()) {
            String agvId = entry.getKey();
            Set<String> usedBackpacks = entry.getValue();
            System.out.println("AGV " + agvId + " 使用背篓: " + usedBackpacks);
        }
        return true;
    }
}
algo-zkd/src/main/java/com/algo/util/AgvTaskUtils.java
New file
@@ -0,0 +1,65 @@
package com.algo.util;
import com.algo.model.AGVStatus;
import com.algo.model.TaskData;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
public class AgvTaskUtils {
    /**
     * 加载和解析AGV状态列表
     *
     * @param filePath 文件地址
     * @return AGV状态列表
     */
    public static List<AGVStatus> loadAgvStatus(String filePath) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 直接解析JSON数组为List<AGVStatus>
            return objectMapper.readValue(
                    new File(filePath),
                    new TypeReference<List<AGVStatus>>() {
                    }
            );
        } catch (FileNotFoundException e) {
            System.err.println("AGV状态列表文件不存在: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("路径AGV状态列表文件读取错误: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("加载AGV状态列表文件失败: " + e.getMessage());
        }
        return null;
    }
    /**
     * 加载和解析任务列表
     *
     * @param filePath 文件地址
     * @return 任务列表
     */
    public static List<TaskData> loadTaskList(String filePath) {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 直接解析JSON数组为List<TaskData>,使用TypeReference指定泛型类型,避免类型擦除导致的转换错误
            return objectMapper.readValue(
                    new File(filePath),
                    new TypeReference<List<TaskData>>() {
                    }
            );
        } catch (FileNotFoundException e) {
            System.err.println("任务列表文件不存在: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("任务列表文件读取错误: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("加载任务列表文件失败: " + e.getMessage());
        }
        return null;
    }
}
algo-zkd/src/main/java/com/algo/util/JsonUtils.java
New file
@@ -0,0 +1,486 @@
package com.algo.util;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * JSON文件读取工具类
 * 用于读取环境配置和路径映射文件
 */
public class JsonUtils {
    /**
     * 读取JSON文件内容
     *
     * @param filePath 文件路径
     * @return JSON字符串内容
     * @throws IOException 文件读取异常
     */
    public static String readJsonFile(String filePath) throws IOException {
        StringBuilder content = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
        }
        return content.toString();
    }
    /**
     * 解析路径映射JSON内容
     * 正确解析path_mapping.json的实际格式:{"path_id_to_coordinates": {...}}
     *
     * @param jsonContent JSON内容
     * @return 路径映射Map,key为路径编号,value为坐标信息
     */
    public static Map<String, Map<String, Integer>> parsePathMapping(String jsonContent) {
        Map<String, Map<String, Integer>> pathMapping = new HashMap<>();
        try {
            // 找到path_id_to_coordinates部分
            String pathIdSection = extractJsonSection(jsonContent, "path_id_to_coordinates");
            if (pathIdSection == null) {
                System.err.println("未找到path_id_to_coordinates部分");
                return pathMapping;
            }
            String[] lines = pathIdSection.split("\n");
            String currentKey = null;
            for (String line : lines) {
                line = line.trim();
                // 查找路径ID
                if (line.startsWith("\"") && line.contains("\":[")) {
                    int endIndex = line.indexOf("\":[");
                    currentKey = line.substring(1, endIndex);
                    pathMapping.put(currentKey, new HashMap<>());
                }
                // 查找坐标信息
                if (currentKey != null && line.contains("\"x\":")) {
                    String xStr = line.substring(line.indexOf("\"x\":") + 4);
                    xStr = xStr.substring(0, xStr.indexOf(",")).trim();
                    try {
                        int x = Integer.parseInt(xStr);
                        pathMapping.get(currentKey).put("x", x);
                    } catch (NumberFormatException e) {
                        // 忽略解析错误
                    }
                }
                if (currentKey != null && line.contains("\"y\":")) {
                    String yStr = line.substring(line.indexOf("\"y\":") + 4);
                    if (yStr.contains(",")) {
                        yStr = yStr.substring(0, yStr.indexOf(",")).trim();
                    } else if (yStr.contains("}")) {
                        yStr = yStr.substring(0, yStr.indexOf("}")).trim();
                    }
                    try {
                        int y = Integer.parseInt(yStr);
                        pathMapping.get(currentKey).put("y", y);
                    } catch (NumberFormatException e) {
                        // 忽略解析错误
                    }
                }
            }
            System.out.println("成功解析路径映射,包含 " + pathMapping.size() + " 个路径点");
        } catch (Exception e) {
            System.err.println("解析路径映射JSON时发生错误: " + e.getMessage());
        }
        return pathMapping;
    }
    /**
     * 加载和解析路径映射文件
     * 正确解析path_mapping.json的实际格式:{"path_id_to_coordinates": {...}}
     *
     * @param filePath 文件地址
     * @return 路径映射Map,key为路径编号,value为坐标信息
     */
    public static Map<String, Map<String, Integer>> loadPathMapping(String filePath) {
        // 转换为目标结构 Map<String, Map<String, Integer>>
        Map<String, Map<String, Integer>> pathMapping = new HashMap<>();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 先解析为顶层Map<String, Object>
            Map<String, Object> topLevelMap = objectMapper.readValue(
                    new File(filePath),
                    Map.class
            );
            if (!topLevelMap.containsKey("path_id_to_coordinates")) {
                System.err.println("未找到path_id_to_coordinates部分");
                return pathMapping;
            }
            // 处理 path_id_to_coordinates(将坐标列表转换为第一个坐标的path_id映射)
            Map<String, Object> pathIdCoords = (Map<String, Object>) topLevelMap.get("path_id_to_coordinates");
            for (Map.Entry<String, Object> entry : pathIdCoords.entrySet()) {
                // 保存方式: 路径ID:{"x": , "y":}
                String pathId = entry.getKey();
                Object coordsObj = entry.getValue();
                if (coordsObj instanceof List) {
                    List<?> coordsList = (List<?>) coordsObj;
                    if (!coordsList.isEmpty()) {
                        Map<?, ?> coordMap = (Map<?, ?>) coordsList.get(0);
                        int x = ((Number) coordMap.get("x")).intValue();
                        int y = ((Number) coordMap.get("y")).intValue();
                        Map<String, Integer> pointMap = new HashMap<>();
                        pointMap.put("x", x);
                        pointMap.put("y", y);
                        pathMapping.put(pathId, pointMap);
                    }
                }
            }
            System.out.println("成功解析路径映射,包含 " + pathMapping.size() + " 个路径点");
        } catch (FileNotFoundException e) {
            System.err.println("路径映射文件不存在: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("路径映射文件读取错误: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("加载路径映射文件失败: " + e.getMessage());
        }
        return pathMapping;
    }
    /**
     * 解析环境配置JSON内容
     * 解析environment.json中的stations信息
     *
     * @param jsonContent JSON内容
     * @return 环境配置Map
     */
    public static Map<String, Object> parseEnvironmentConfig(String jsonContent) {
        Map<String, Object> config = new HashMap<>();
        try {
            // 解析width
            if (jsonContent.contains("\"width\":")) {
                String widthStr = jsonContent.substring(jsonContent.indexOf("\"width\":") + 8);
                widthStr = widthStr.substring(0, widthStr.indexOf(",")).trim();
                try {
                    config.put("width", Integer.parseInt(widthStr));
                } catch (NumberFormatException e) {
                    config.put("width", 78);
                }
            }
            // 解析height
            if (jsonContent.contains("\"height\":")) {
                String heightStr = jsonContent.substring(jsonContent.indexOf("\"height\":") + 9);
                heightStr = heightStr.substring(0, heightStr.indexOf(",")).trim();
                try {
                    config.put("height", Integer.parseInt(heightStr));
                } catch (NumberFormatException e) {
                    config.put("height", 50);
                }
            }
            // 解析stations信息
            Map<String, Map<String, Object>> stations = parseStations(jsonContent);
            config.put("stations", stations);
            config.put("stationCount", stations.size());
            System.out.println("成功解析环境配置,包含 " + stations.size() + " 个工作站");
        } catch (Exception e) {
            System.err.println("解析环境配置JSON时发生错误: " + e.getMessage());
        }
        return config;
    }
    /**
     * 加载和解析环境配置文件
     * 解析environment.json中的stations信息
     *
     * @param filePath 文件地址
     * @return 环境配置Map
     */
    public static Map<String, Object> loadEnvironment(String filePath) {
        // 转换为目标结构 Map<String, Object>
        Map<String, Object> environmentMap = new HashMap<>();
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            // 先解析为顶层Map<String, Object>
            Map<String, Object> topLevelMap = objectMapper.readValue(
                    new File(filePath),
                    Map.class
            );
            // 解析width
            if (topLevelMap.containsKey("width")) {
                environmentMap.put("width", Integer.parseInt(topLevelMap.get("width").toString()));
            } else {
                environmentMap.put("width", 78);
            }
            // 解析height
            if (topLevelMap.containsKey("width")) {
                environmentMap.put("height", Integer.parseInt(topLevelMap.get("height").toString()));
            } else {
                environmentMap.put("height", 50);
            }
            // 解析stations信息
            if (topLevelMap.containsKey("stations")) {
                Map<String, Map<String, Object>> stations = new HashMap<>();
                Map<String, Object> stationMap = (Map<String, Object>) topLevelMap.get("stations");
                for (Map.Entry<String, Object> stationEntry : stationMap.entrySet()) {
                    // 工作站ID
                    String pathId = stationEntry.getKey();
                    Map<String, Object> stationInfo = (Map<String, Object>) stationEntry.getValue();
                    Map<String, Object> map = new HashMap<>();
                    // 解析capacity
                    if (stationInfo.containsKey("capacity")) {
                        map.put("capacity", Integer.parseInt(stationInfo.get("capacity").toString()));
                    }
                    // 解析load_position和unload_position
                    if (stationInfo.containsKey("load_position")) {
                        List<Integer> loadPos = (List<Integer>) stationInfo.get("load_position");
                        map.put("load_position", loadPos);
                    }
                    if (stationInfo.containsKey("unload_position")) {
                        List<Integer> unloadPos = (List<Integer>) stationInfo.get("unload_position");
                        map.put("unload_position", unloadPos);
                    }
                    stations.put(pathId, map);
                }
                environmentMap.put("stations", stations);
                environmentMap.put("stationCount", stations.size());
                System.out.println("成功解析环境配置,包含 " + stations.size() + " 个工作站");
            }
        } catch (FileNotFoundException e) {
            System.err.println("环境配置文件不存在: " + e.getMessage());
        } catch (IOException e) {
            System.err.println("环境配置文件读取错误: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("加载环境配置文件失败: " + e.getMessage());
        }
        return environmentMap;
    }
    /**
     * 解析stations信息
     *
     * @param jsonContent JSON内容
     * @return stations Map
     */
    private static Map<String, Map<String, Object>> parseStations(String jsonContent) {
        Map<String, Map<String, Object>> stations = new HashMap<>();
        try {
            String stationsSection = extractJsonSection(jsonContent, "stations");
            if (stationsSection == null) {
                return stations;
            }
            String[] lines = stationsSection.split("\n");
            String currentStation = null;
            for (String line : lines) {
                line = line.trim();
                // 查找工作站ID
                if (line.startsWith("\"") && line.contains("\":{")) {
                    int endIndex = line.indexOf("\":{");
                    currentStation = line.substring(1, endIndex);
                    stations.put(currentStation, new HashMap<>());
                }
                // 解析capacity
                if (currentStation != null && line.contains("\"capacity\":")) {
                    String capacityStr = line.substring(line.indexOf("\"capacity\":") + 11);
                    capacityStr = capacityStr.substring(0, capacityStr.indexOf(",")).trim();
                    try {
                        int capacity = Integer.parseInt(capacityStr);
                        stations.get(currentStation).put("capacity", capacity);
                    } catch (NumberFormatException e) {
                        // 忽略解析错误
                    }
                }
                // 解析load_position和unload_position
                if (currentStation != null && line.contains("\"load_position\":")) {
                    List<Integer> loadPos = parsePosition(stationsSection, currentStation, "load_position");
                    if (loadPos != null) {
                        stations.get(currentStation).put("load_position", loadPos);
                    }
                }
                if (currentStation != null && line.contains("\"unload_position\":")) {
                    List<Integer> unloadPos = parsePosition(stationsSection, currentStation, "unload_position");
                    if (unloadPos != null) {
                        stations.get(currentStation).put("unload_position", unloadPos);
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("解析stations信息时发生错误: " + e.getMessage());
        }
        return stations;
    }
    /**
     * 解析位置信息
     *
     * @param content      JSON内容
     * @param stationId    工作站ID
     * @param positionType 位置类型(load_position或unload_position)
     * @return 位置坐标列表
     */
    private static List<Integer> parsePosition(String content, String stationId, String positionType) {
        try {
            String stationSection = content.substring(content.indexOf("\"" + stationId + "\":{"));
            String positionSection = stationSection.substring(stationSection.indexOf("\"" + positionType + "\":"));
            String[] lines = positionSection.split("\n");
            List<Integer> position = new ArrayList<>();
            for (String line : lines) {
                line = line.trim();
                if (line.matches("\\d+,?")) {
                    String numStr = line.replaceAll("[,\\s]", "");
                    try {
                        position.add(Integer.parseInt(numStr));
                    } catch (NumberFormatException e) {
                        // 忽略解析错误
                    }
                }
                if (line.contains("]")) {
                    break;
                }
            }
            return position.size() == 2 ? position : null;
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 提取JSON中的特定部分
     *
     * @param jsonContent JSON内容
     * @param sectionName 部分名称
     * @return 提取的部分内容
     */
    private static String extractJsonSection(String jsonContent, String sectionName) {
        try {
            String startPattern = "\"" + sectionName + "\": {";
            int startIndex = jsonContent.indexOf(startPattern);
            if (startIndex == -1) {
                return null;
            }
            int braceCount = 0;
            int currentIndex = startIndex + startPattern.length() - 1;
            while (currentIndex < jsonContent.length()) {
                char ch = jsonContent.charAt(currentIndex);
                if (ch == '{') {
                    braceCount++;
                } else if (ch == '}') {
                    braceCount--;
                    if (braceCount == 0) {
                        break;
                    }
                }
                currentIndex++;
            }
            return jsonContent.substring(startIndex + startPattern.length() - 1, currentIndex + 1);
        } catch (Exception e) {
            return null;
        }
    }
    /**
     * 获取路径点的坐标
     *
     * @param pathId      路径点ID
     * @param pathMapping 路径映射
     * @return 坐标数组 [x, y],如果未找到返回null
     */
    public static int[] getCoordinate(String pathId, Map<String, Map<String, Integer>> pathMapping) {
        Map<String, Integer> coordMap = pathMapping.get(pathId);
        if (coordMap != null && coordMap.containsKey("x") && coordMap.containsKey("y")) {
            return new int[]{coordMap.get("x"), coordMap.get("y")};
        }
        return null;
    }
    /**
     * 获取工作站信息
     *
     * @param stationId         工作站ID
     * @param environmentConfig 环境配置
     * @return 工作站信息Map
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> getStationInfo(String stationId, Map<String, Object> environmentConfig) {
        Map<String, Map<String, Object>> stations =
                (Map<String, Map<String, Object>>) environmentConfig.get("stations");
        if (stations != null) {
            return stations.get(stationId);
        }
        return null;
    }
    /**
     * 判断位置是否为工作站
     *
     * @param position          位置字符串
     * @param environmentConfig 环境配置
     * @return 是否为工作站
     */
    public static boolean isStation(String position, Map<String, Object> environmentConfig) {
        return getStationInfo(position, environmentConfig) != null;
    }
    /**
     * 计算两点之间的曼哈顿距离
     *
     * @param coord1 坐标1 [x, y]
     * @param coord2 坐标2 [x, y]
     * @return 曼哈顿距离
     */
    public static double calculateManhattanDistance(int[] coord1, int[] coord2) {
        if (coord1 == null || coord2 == null || coord1.length != 2 || coord2.length != 2) {
            return Double.MAX_VALUE;
        }
        return Math.abs(coord1[0] - coord2[0]) + Math.abs(coord1[1] - coord2[1]);
    }
    /**
     * 计算两点之间的欧几里得距离
     *
     * @param coord1 坐标1 [x, y]
     * @param coord2 坐标2 [x, y]
     * @return 欧几里得距离
     */
    public static double calculateEuclideanDistance(int[] coord1, int[] coord2) {
        if (coord1 == null || coord2 == null || coord1.length != 2 || coord2.length != 2) {
            return Double.MAX_VALUE;
        }
        int dx = coord1[0] - coord2[0];
        int dy = coord1[1] - coord2[1];
        return Math.sqrt(dx * dx + dy * dy);
    }
}
algo-zkd/src/main/resources/META-INF/spring.factories
New file
@@ -0,0 +1,2 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.algo.service.PathPlanningService
algo-zkd/src/main/resources/application.yml
New file
@@ -0,0 +1,8 @@
spring:
  application:
    name: web-server
server:
  port: 8083
api/pom.xml
New file
@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zy</groupId>
    <artifactId>api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <dubbo.version>2.7.8</dubbo.version>
        <curator.version>4.2.0</curator.version>
        <curator-recipes.version>2.8.0</curator-recipes.version>
        <zookeeper.version>3.6.3</zookeeper.version>
    </properties>
    <dependencies>
        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>2.7.8</version>
        </dependency>
        <!-- Dubbo Spring Boot Starter -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>
        <!-- zookeeper注册中心 需要导入zookeeper依赖  -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>${zookeeper.version}</version>
            <!-- 提示与zookeeper中的slf4j-log4j12-1.7.29.jar 包冲突-->
            <!--排除这个slf4j-log4j12-->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>log4j</groupId>
                    <artifactId>log4j</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- Zookeeper 客户端 -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>${curator.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>${curator-recipes.version}</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.5.12</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
api/src/main/java/com/algo/entity/User.java
New file
@@ -0,0 +1,25 @@
package com.algo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
@Data
@TableName("sys_user")
public class User implements Serializable {
    private String username;
    /**
     * 密码
     */
    private String password;
    /**
     * 昵称
     */
    private String nickname;
}
api/src/main/java/com/algo/service/UserService.java
New file
@@ -0,0 +1,20 @@
package com.algo.service;
import com.algo.entity.User;
import java.util.List;
//todo 这里写一个自定义注解,之后通过自定义注解来获取该接口和实现类将其注册到注册中心
public interface UserService {
    /**
     * 查询user
     */
    public User queryUser();
    void add(String key, String value);
    String get(String key);
    List<User> getDataByMySql();
}
rpc-server/pom.xml
New file
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zy</groupId>
    <artifactId>rpc-server</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zy</groupId>
            <artifactId>service</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
rpc-server/src/main/java/com/algo/SpringBootRpcServerApplication.java
New file
@@ -0,0 +1,17 @@
package com.algo;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class SpringBootRpcServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootRpcServerApplication.class, args);
    }
}
rpc-server/src/main/resources/application.yml
New file
@@ -0,0 +1,85 @@
spring:
  application:
    name: rpc-server
  mvc:
    static-path-pattern: /**
  servlet:
    multipart:
      maxFileSize: 100MB
      maxRequestSize: 100MB
  jmx:
    enabled: false
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/jbly?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: xltys1995
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 30000
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      test-while-idle: true
      test-on-borrow: true
      test-on-return: false
      remove-abandoned: true
      remove-abandoned-timeout: 1800
      #pool-prepared-statements: false
      #max-pool-prepared-statement-per-connection-size: 20
      filters: stat, wall
      validation-query: SELECT 'x'
      aop-patterns: com.zy.*.*.service.*
      stat-view-servlet:
        url-pattern: /druid/*
        reset-enable: true
        login-username: admin
        login-password: admin
        enabled: true
  redis:
    host: localhost
    password: xltys1995
    port: 6379
    max: 30
    min: 10
    timeout: 5000
    index: 3
server:
  port: 8082
dubbo:
  application:
    name: rpc-server
  registry:
    address: zookeeper://127.0.0.1:2181
    # protocol: zookeeper
    # password: dreamtech
    # username: zk_user
  protocol:
    name: dubbo
    port: 20880
#  monitor:
#    protocol: registry
mybatis-plus:
  mapper-locations: classpath:mapper/*/*.xml
  #  global-config:
  #    field-strategy: 0
  configuration:
    #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
    cache-enabled: true
  global-config:
    :banner: false
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0
rpc-server/target/classes/application.yml
@@ -39,6 +39,12 @@
        login-username: admin
        login-password: admin
        enabled: true
  servlet:
    multipart:
      maxFileSize: 100MB
      maxRequestSize: 100MB
  jmx:
    enabled: false
  redis:
    host: localhost
@@ -49,37 +55,3 @@
    timeout: 5000
    index: 3
server:
  port: 8082
dubbo:
  application:
    name: rpc-server
  registry:
    address: zookeeper://127.0.0.1:2181
    # protocol: zookeeper
    # password: dreamtech
    # username: zk_user
  protocol:
    name: dubbo
    port: 20880
#  monitor:
#    protocol: registry
mybatis-plus:
  mapper-locations: classpath:mapper/*/*.xml
  #  global-config:
  #    field-strategy: 0
  configuration:
    #    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true
    cache-enabled: true
  global-config:
    :banner: false
    db-config:
      id-type: auto
      logic-delete-value: 1
      logic-not-delete-value: 0
service/pom.xml
New file
@@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zy</groupId>
    <artifactId>service</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.zy</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 集成redis依赖  -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.0.3.RELEASE</version>
        </dependency>
        <!-- mybatis-plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.6</version>
        </dependency>
    </dependencies>
</project>
service/src/main/java/com/algo/mapper/UserMapper.java
New file
@@ -0,0 +1,13 @@
package com.algo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.algo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper extends BaseMapper<User> {
}
service/src/main/java/com/algo/support/UserServiceSupport.java
New file
@@ -0,0 +1,49 @@
package com.algo.support;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.algo.entity.User;
import com.algo.mapper.UserMapper;
import com.algo.service.UserService;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.List;
@DubboService // 该注解用于将当前类对象存入spring容器中,既可以本项目服务使用,父接口注解能力也可以提供给其他项目使用
public class UserServiceSupport implements UserService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private UserMapper userMapper;
    /**
     * 查询user
     */
    @Override
    public User queryUser() {
        User user = new User();
        user.setNickname("1212");
        user.setUsername("张三");
        return user;
    }
    @Override
    public void add(String key, String value) {
        redisTemplate.opsForValue().set(key, value);
    }
    @Override
    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    @Override
    public List<User> getDataByMySql() {
        List<User> users = userMapper.selectList(new QueryWrapper<>());
        return users;
    }
}
service/src/main/resources/mapper/system/UserMapper.xml
web-server/pom.xml
New file
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.zy</groupId>
    <artifactId>web-server</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.zy</groupId>
            <artifactId>api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
web-server/src/main/java/com/algo/SpringBootAplication.java
New file
@@ -0,0 +1,17 @@
package com.algo;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableDubbo
public class SpringBootAplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootAplication.class, args);
    }
}
web-server/src/main/java/com/algo/controller/UserController.java
New file
@@ -0,0 +1,41 @@
package com.algo.controller;
import com.algo.entity.User;
import com.algo.service.UserService;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
    @DubboReference(timeout = 1000, check = false)//远程注入
    private UserService userService;
    @GetMapping("/user")
    public String user() {
        User user = userService.queryUser();
        return user.toString();
    }
    @GetMapping("/get")
    public String get() {
        return userService.get("user");
    }
    @GetMapping("/add")
    public String add() {
        userService.add("user", "123");
        return "ok";
    }
    @GetMapping("/user3")
    public List<User> user3() {
        return userService.getDataByMySql();
    }
}
web-server/src/main/resources/application.yml
New file
@@ -0,0 +1,19 @@
spring:
  application:
    name: web-server
server:
  port: 8083
dubbo:
  protocol:
    name: dubbo
    port: 20885
  application:
    name: web-server
  registry:
    address: zookeeper://127.0.0.1:2181
    # password: dreamtech
    # username: zk_user
#  monitor:
#    protocol: registry