test.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # ===============================================================================
  4. #
  5. # test.py
  6. #
  7. # Test program for the simple GL Viewer.
  8. #
  9. # Copyright (c) 2011, Arne Schmitz <arne.schmitz@gmx.net>
  10. # All rights reserved.
  11. #
  12. # Redistribution and use in source and binary forms, with or without
  13. # modification, are permitted provided that the following conditions are met:
  14. # * Redistributions of source code must retain the above copyright
  15. # notice, this list of conditions and the following disclaimer.
  16. # * Redistributions in binary form must reproduce the above copyright
  17. # notice, this list of conditions and the following disclaimer in the
  18. # documentation and/or other materials provided with the distribution.
  19. # * Neither the name of the <organization> nor the
  20. # names of its contributors may be used to endorse or promote products
  21. # derived from this software without specific prior written permission.
  22. #
  23. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  24. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. # DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
  27. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. #
  34. # ===============================================================================
  35. import math
  36. import threading
  37. import time
  38. import sys
  39. from PyQt5.QtGui import *
  40. from PyQt5.QtWidgets import *
  41. from PyQt5.QtCore import *
  42. from MapGLWidget import MapGLWidget
  43. import json
  44. import dijkstra.Map as mp
  45. import ControllWidget
  46. import JointContrallerWidget
  47. import ManualOperationWidget
  48. import RobotData as RD
  49. import message_pb2 as message
  50. import google.protobuf.json_format as jtf
  51. import google.protobuf.text_format as tf
  52. import uuid
  53. from mytool.txt_helper.txt_operation import TXTOperation
  54. from custom_define import RobotName
  55. from mytool.RabbitMq_helper import async_communication as rabitmq
  56. # ===============================================================================
  57. class MainWindow(QMainWindow):
  58. """docstring for Mainwindow"""
  59. djks_map_ = mp.MapManager()
  60. ui_nodes = {}
  61. ui_nodes["street_nodes"] = []
  62. ui_nodes["space_nodes"] = []
  63. def __init__(self, parent=None):
  64. super(MainWindow, self).__init__(parent)
  65. self.basic()
  66. self.count_frame_ = ControllWidget.CountFrame()
  67. self.LoadMap()
  68. self.isLocalTest = False
  69. self.isAutoDispatchOnline = True
  70. rpc_1 = "192.168.0.70:9090"
  71. mqtt_1 = ["pyui1", "192.168.0.70", 1883, "admin", "zx123456"]
  72. rpc_2 = "192.168.0.71:9090"
  73. mqtt_2 = ["pyui2", "192.168.0.71", 1883, "admin", "zx123456"]
  74. if self.isLocalTest:
  75. rpc_1 = "127.0.0.1:9090"
  76. mqtt_1 = ["pyui-main", "127.0.0.1", 1883, "admin", "zx123456"]
  77. rpc_2 = "127.0.0.1:9091"
  78. mqtt_2 = ["pyui-child", "127.0.0.1", 1883, "admin", "zx123456"]
  79. config1 = {"label": RobotName.strAGVMain,
  80. "rpc": rpc_1,
  81. "street_nodes": self.ui_nodes["street_nodes"],
  82. "space_nodes": self.ui_nodes["space_nodes"],
  83. "mqtt": mqtt_1,
  84. "subTopics": {"agv_main/agv_statu": message.RobotStatu.__name__,
  85. "agv_main/nav_statu": message.NavStatu.__name__},
  86. "cmdTopic": "agv_main/nav_cmd",
  87. "robotColor": [0.7, 0.2, 0.3]}
  88. config2 = {"label": RobotName.strAGV2,
  89. "rpc": rpc_2,
  90. # "rpc": "127.0.0.1:9091",
  91. "street_nodes": self.ui_nodes["street_nodes"],
  92. "space_nodes": self.ui_nodes["space_nodes"],
  93. "mqtt": mqtt_2,
  94. "subTopics": {"agv_child/agv_statu": message.RobotStatu.__name__,
  95. "agv_child/nav_statu": message.NavStatu.__name__},
  96. "cmdTopic": "agv_child/nav_cmd",
  97. "robotColor": [0.4, 0.2, 0.8]}
  98. self.Controller1 = ControllWidget.ControlFrame(config1)
  99. self.Controller2 = ControllWidget.ControlFrame(config2)
  100. robot_dict = {self.Controller1.robot_.name_: self.Controller1.robot_,
  101. self.Controller2.robot_.name_: self.Controller2.robot_}
  102. self.ManualOperationWidget = ManualOperationWidget.ManualOperationWidget(robot_dict)
  103. splitter_main = self.split_()
  104. self.setCentralWidget(splitter_main)
  105. self.gl.SetRobot1Instance(self.Controller1.robot_)
  106. self.gl.SetRobot2Instance(self.Controller2.robot_)
  107. self.timer_ = QTimer()
  108. self.timer_.timeout.connect(self.Update)
  109. self.timer_.start(100)
  110. # 启动RabbitMQ
  111. self.g_rabbitmq = None
  112. if self.isAutoDispatchOnline:
  113. self.g_rabbitmq = rabitmq.RabbitAsyncCommunicator("192.168.0.201", 5672, "zx", "123456")
  114. calbbacks = [["agv_park_command_request_queue", self.recv_park_request],
  115. ["agv_pick_command_request_queue", self.recv_pick_request]]
  116. self.g_rabbitmq.Init(calbbacks, None)
  117. self.g_rabbitmq.setDaemon(True) # 守护线程随主线程结束
  118. self.g_rabbitmq.start()
  119. def recv_park_request(self, msg):
  120. print("Recv_park_request------------------\n", msg)
  121. park_table = message.park_table()
  122. try:
  123. tf.Parse(msg, park_table)
  124. except Exception as e:
  125. print("Parse error\n")
  126. # split_msg = msg.split(' ')
  127. # result = [float(split_msg[0]), float(split_msg[1]), float(split_msg[2])]
  128. if self.isAutoDispatchOnline:
  129. self.AutoParkTask(park_table)
  130. def recv_pick_request(self, msg):
  131. print("Recv_pick_request------------------\n", msg)
  132. park_table = message.park_table()
  133. return
  134. def AutoParkTask(self, park_table: message.park_table = None):
  135. print("AutoParkTask:---------------------\n")
  136. self.djks_map_.Reset() # 重置地图
  137. entrance_id = "S" + str(park_table.terminal_id) # "S1101"
  138. entrance_x, entrance_y, entrance_theta = [park_table.entrance_measure_info.measure_info_to_plc_forward.cx,
  139. park_table.entrance_measure_info.measure_info_to_plc_forward.cy,
  140. (
  141. 90 + park_table.entrance_measure_info.measure_info_to_plc_forward.theta) / 180 * math.pi]
  142. # 变换到地图坐标系
  143. [dx, dy, da] = [-0.223411843181, -0.643030941486, 178.9478 / 180 * math.pi]
  144. trans_x = -0.99983137 * entrance_x - 0.01836309 * entrance_y + dx
  145. trans_y = 0.01836309 * entrance_x - 0.99983137 * entrance_y + dy
  146. trans_a = entrance_theta + da - math.pi
  147. while trans_a < 0:
  148. trans_a += math.pi
  149. print("entrance:", entrance_id, trans_x, trans_y, trans_a / math.pi * 180)
  150. target_id = "S" + str(park_table.allocated_space_info.table_id) # "S147"
  151. print("target:", target_id)
  152. wheel_base = park_table.entrance_measure_info.measure_info_to_plc_forward.wheelbase
  153. self.Controller1.WheelBaseEdit.setText(str(wheel_base))
  154. self.Controller2.WheelBaseEdit.setText(str(wheel_base))
  155. trans_x += wheel_base / 2 * math.cos(trans_a)
  156. trans_y += wheel_base / 2 * math.sin(trans_a)
  157. self.djks_map_.ResetSpaceNode(entrance_id, trans_x, trans_y) # 更新库位点
  158. entrance_street_node = self.ComputeStreetNode(entrance_id, trans_x, trans_y, trans_a)
  159. self.djks_map_.ResetStreetNode("Input_R1101", entrance_street_node[0], entrance_street_node[1]) # 更新库位点对应马路点
  160. print("entrance_space pose: ", self.djks_map_.map_t.graph_.points[entrance_id])
  161. print("entrance_street pose ", self.djks_map_.map_t.graph_.points["Input_R1101"])
  162. # 加临时边
  163. self.djks_map_.AddEdge_t(entrance_id, "Input_R1101")
  164. # 加临时边
  165. for node_id, value in self.djks_map_.VertexDict().items():
  166. if node_id.find("Input") >= 0:
  167. self.djks_map_.AddEdge_t("Input_R1101", node_id)
  168. # # 开始系列子流程-----------------------------------------------------
  169. # # 主车进入口
  170. # cur_main_pose = self.Controller1.robot_.timedRobotStatu_.statu
  171. # [label, pt] = mp.MapManager().findNeastNode([cur_main_pose.x, cur_main_pose.y])
  172. # node = mp.MapManager().GetVertex(label)# StreetNode
  173. # self.Controller1.robot_.Navigatting(label, entrance_id, True, wheel_base)
  174. # # 副车进入口
  175. # cur_child_pose = self.Controller2.robot_.timedRobotStatu_.statu
  176. # [label, pt] = mp.MapManager().findNeastNode([cur_child_pose.x, cur_child_pose.y])
  177. # node = mp.MapManager().GetVertex(label)# StreetNode
  178. # self.Controller2.robot_.Navigatting(label, entrance_id, True, wheel_base)
  179. # # 切换整车模式
  180. # self.Controller1.robot_.MainAgvchangecb()
  181. # # 整车进目标车位
  182. # self.Controller1.robot_.Navigatting(entrance_id, target_id, True, wheel_base)
  183. # #
  184. # 恢复
  185. # self.djks_map_.Reset()
  186. def ComputeStreetNode(self, s_id, s_x, s_y, s_theta):
  187. """
  188. """
  189. n_x, n_y = 0, 0
  190. if s_id == "S1101":
  191. n_y = self.djks_map_.map_t.graph_.points["Input_R1100"][1]
  192. k = math.tan(s_theta)
  193. n_x = (n_y - s_y) / k + s_x # 弧度
  194. # print(n_x, n_y)
  195. return [n_x, n_y]
  196. def LoadMap(self):
  197. self.gl = MapGLWidget() # 将opengl例子嵌入GUI
  198. map = self.load_map("./map.json")
  199. for node in map['street_nodes'].items():
  200. [id, point] = node
  201. street_node = mp.StreetNode(id, point[0], point[1])
  202. self.djks_map_.AddVertex(street_node)
  203. self.gl.AddNode([id, "street_node", point])
  204. self.ui_nodes["street_nodes"].append(id)
  205. for node in map['space_nodes'].items():
  206. [id, point] = node
  207. [x, y, yaw] = point
  208. space_node = mp.SpaceNode(id, point[0], point[1], yaw)
  209. self.djks_map_.AddVertex(space_node)
  210. glNode = [id, "space_node", [x, y]]
  211. self.gl.AddNode(glNode)
  212. self.ui_nodes["space_nodes"].append(id)
  213. for road in map['roads'].items():
  214. self.gl.AddRoad(road)
  215. [_, points] = road
  216. for pt1 in points:
  217. for pt2 in points:
  218. if not pt1 == pt2:
  219. self.djks_map_.AddEdge(pt1, pt2)
  220. def load_map(self, file):
  221. with open(file, 'r', encoding='utf-8') as fp:
  222. map = json.load(fp)
  223. return map
  224. def Update(self):
  225. self.gl.update()
  226. if self.isAutoDispatchOnline:
  227. # self.Controller1.setEnabled(False)
  228. # self.Controller2.setEnabled(False)
  229. pass
  230. else:
  231. self.Controller1.setVisible(True)
  232. self.Controller2.setVisible(True)
  233. # 窗口基础属性
  234. def basic(self):
  235. # 设置标题,大小,图标
  236. self.setWindowTitle("GT")
  237. self.resize(1100, 650)
  238. self.setWindowIcon(QIcon("./image/Gt.png"))
  239. # 居中显示
  240. screen = QDesktopWidget().geometry()
  241. self_size = self.geometry()
  242. self.move(int((screen.width() - self_size.width()) / 2), int((screen.height() - self_size.height()) / 2))
  243. def closeEvent(self, QCloseEvent):
  244. self.gl.close()
  245. print("close...")
  246. # 分割窗口
  247. def split_(self):
  248. splitter_main = QSplitter(Qt.Horizontal)
  249. splitter = QSplitter(Qt.Vertical)
  250. splitter.addWidget(self.count_frame_)
  251. splitter.addWidget(self.Controller1)
  252. splitter.addWidget(self.Controller2)
  253. splitter.addWidget(self.ManualOperationWidget)
  254. splitter.setStretchFactor(0, 1)
  255. splitter.setStretchFactor(1, 3)
  256. splitter.setStretchFactor(2, 3)
  257. splitter.setStretchFactor(3, 1)
  258. splitter_main.addWidget(splitter)
  259. splitter_main.addWidget(self.gl)
  260. splitter_main.setStretchFactor(0, 1)
  261. splitter_main.setStretchFactor(2, 4)
  262. return splitter_main
  263. if __name__ == "__main__":
  264. app = QApplication(sys.argv)
  265. win = MainWindow()
  266. win.show()
  267. sys.exit(app.exec_())
  268. # ===============================================================================
  269. # Main
  270. # ===============================================================================
  271. '''app = QApplication(sys.argv)
  272. mainWindow = MapGLWidget()
  273. mainWindow.show()
  274. mainWindow.raise_() # Need this at least on OS X, otherwise the window ends up in background
  275. sys.exit(app.exec_())'''
  276. # ===============================================================================
  277. #
  278. # Local Variables:
  279. # mode: Python
  280. # indent-tabs-mode: nil
  281. # End:
  282. #
  283. # ===============================================================================