三一平台.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. import sys
  2. import hashlib
  3. import requests
  4. import json
  5. import time
  6. from datetime import datetime, timedelta
  7. from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
  8. QHBoxLayout, QLabel, QLineEdit, QPushButton,
  9. QTableWidget, QTableWidgetItem, QHeaderView,
  10. QCheckBox, QMessageBox, QGroupBox, QTextEdit,
  11. QGridLayout, QSplitter)
  12. from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
  13. from PyQt5.QtGui import QTextDocument
  14. class LoginThread(QThread):
  15. """登录线程"""
  16. finished = pyqtSignal(dict)
  17. error = pyqtSignal(str)
  18. def __init__(self, username, password):
  19. super().__init__()
  20. self.username = username
  21. self.password = password
  22. def run(self):
  23. try:
  24. md5_password = hashlib.md5(self.password.encode('utf-8')).hexdigest()
  25. url = f"http://119.91.58.175:9999/gps-web/api/login.jsp?userId={self.username}&password={md5_password}&loginType=user&onlyValidate=false&plateColor=&loginWay=interface&loginLang=zh_CN"
  26. response = requests.get(url, timeout=10)
  27. response.encoding = 'utf-8'
  28. if response.status_code == 200:
  29. data = response.json()
  30. if data.get('rspCode') == '1':
  31. self.finished.emit(data)
  32. else:
  33. self.error.emit(f"登录失败: {data.get('msg', '未知错误')}")
  34. else:
  35. self.error.emit(f"请求失败: HTTP {response.status_code}")
  36. except Exception as e:
  37. self.error.emit(f"登录异常: {str(e)}")
  38. class QueryThread(QThread):
  39. """查询车辆定位线程"""
  40. finished = pyqtSignal(dict)
  41. error = pyqtSignal(str)
  42. def __init__(self, session_id):
  43. super().__init__()
  44. self.session_id = session_id
  45. def run(self):
  46. try:
  47. url = f"http://119.91.58.175:9999/gps-web/api/get_gps_r.jsp?sessionId={self.session_id}&teamId=&carIds=&simple=false"
  48. response = requests.get(url, timeout=15)
  49. response.encoding = 'utf-8'
  50. if response.status_code == 200:
  51. data = response.json()
  52. if data.get('rspCode') == '1':
  53. self.finished.emit(data)
  54. else:
  55. self.error.emit(f"查询失败: {data.get('msg', '未知错误')}")
  56. else:
  57. self.error.emit(f"请求失败: HTTP {response.status_code}")
  58. except Exception as e:
  59. self.error.emit(f"查询异常: {str(e)}")
  60. class MainWindow(QMainWindow):
  61. def __init__(self):
  62. super().__init__()
  63. self.session_id = None
  64. self.username = None
  65. self.car_data = []
  66. self.car_checked_state = {} # 存储每辆车的勾选状态
  67. self.triggered_records = {} # 存储触发记录 {car_plate: {'load': last_time, 'unload': last_time}}
  68. self.is_monitoring = False
  69. self.refresh_timer = QTimer()
  70. self.refresh_timer.timeout.connect(self.auto_refresh)
  71. self.initUI()
  72. def initUI(self):
  73. self.setWindowTitle('车辆定位监控系统')
  74. self.setGeometry(100, 100, 1400, 800)
  75. # 中央窗口
  76. central_widget = QWidget()
  77. self.setCentralWidget(central_widget)
  78. main_layout = QVBoxLayout(central_widget)
  79. # 登录区域
  80. login_group = QGroupBox("登录")
  81. login_layout = QHBoxLayout()
  82. login_layout.addWidget(QLabel("账号:"))
  83. self.username_input = QLineEdit()
  84. self.username_input.setPlaceholderText("请输入账号")
  85. self.username_input.setText("深金川")
  86. login_layout.addWidget(self.username_input)
  87. login_layout.addWidget(QLabel("密码:"))
  88. self.password_input = QLineEdit()
  89. self.password_input.setPlaceholderText("请输入密码")
  90. self.password_input.setEchoMode(QLineEdit.Password)
  91. self.password_input.setText("123456")
  92. login_layout.addWidget(self.password_input)
  93. self.login_btn = QPushButton("登录")
  94. self.login_btn.clicked.connect(self.handle_login)
  95. login_layout.addWidget(self.login_btn)
  96. self.status_label = QLabel("未登录")
  97. login_layout.addWidget(self.status_label)
  98. login_layout.addStretch()
  99. login_group.setLayout(login_layout)
  100. main_layout.addWidget(login_group)
  101. # 地址输入区域
  102. addr_group = QGroupBox("地址监控设置")
  103. addr_layout = QGridLayout()
  104. addr_layout.addWidget(QLabel("装车地址:"), 0, 0)
  105. self.load_addr_input = QLineEdit()
  106. self.load_addr_input.setPlaceholderText("请输入装车地址关键词")
  107. addr_layout.addWidget(self.load_addr_input, 0, 1)
  108. addr_layout.addWidget(QLabel("卸车地址:"), 0, 2)
  109. self.unload_addr_input = QLineEdit()
  110. self.unload_addr_input.setPlaceholderText("请输入卸车地址关键词")
  111. addr_layout.addWidget(self.unload_addr_input, 0, 3)
  112. # 控制按钮
  113. self.start_btn = QPushButton("启动监控")
  114. self.start_btn.clicked.connect(self.start_monitoring)
  115. self.start_btn.setEnabled(False)
  116. addr_layout.addWidget(self.start_btn, 0, 4)
  117. self.stop_btn = QPushButton("停止监控")
  118. self.stop_btn.clicked.connect(self.stop_monitoring)
  119. self.stop_btn.setEnabled(False)
  120. addr_layout.addWidget(self.stop_btn, 0, 5)
  121. self.manual_query_btn = QPushButton("手动刷新")
  122. self.manual_query_btn.clicked.connect(self.handle_query)
  123. self.manual_query_btn.setEnabled(False)
  124. addr_layout.addWidget(self.manual_query_btn, 0, 6)
  125. addr_layout.addWidget(QLabel("刷新间隔:5秒"), 0, 7)
  126. addr_group.setLayout(addr_layout)
  127. main_layout.addWidget(addr_group)
  128. # 分割器:表格和日志
  129. splitter = QSplitter(Qt.Vertical)
  130. # 车辆信息表格
  131. table_widget = QWidget()
  132. table_layout = QVBoxLayout(table_widget)
  133. table_layout.setContentsMargins(0, 0, 0, 0)
  134. self.table = QTableWidget()
  135. self.table.setColumnCount(5)
  136. self.table.setHorizontalHeaderLabels(["选择", "车队名", "车牌号", "在线状态", "定位地址"])
  137. self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
  138. self.table.setAlternatingRowColors(True)
  139. table_layout.addWidget(self.table)
  140. # 表格按钮
  141. table_btn_layout = QHBoxLayout()
  142. self.select_all_btn = QPushButton("全选")
  143. self.select_all_btn.clicked.connect(self.select_all)
  144. table_btn_layout.addWidget(self.select_all_btn)
  145. self.deselect_all_btn = QPushButton("取消全选")
  146. self.deselect_all_btn.clicked.connect(self.deselect_all)
  147. table_btn_layout.addWidget(self.deselect_all_btn)
  148. table_btn_layout.addStretch()
  149. table_layout.addLayout(table_btn_layout)
  150. splitter.addWidget(table_widget)
  151. # 日志输出框
  152. log_widget = QWidget()
  153. log_layout = QVBoxLayout(log_widget)
  154. log_layout.setContentsMargins(0, 0, 0, 0)
  155. log_label = QLabel("日志输出")
  156. log_layout.addWidget(log_label)
  157. self.log_output = QTextEdit()
  158. self.log_output.setReadOnly(True)
  159. # 限制日志行数 - 使用document().setMaximumBlockCount()
  160. self.log_output.document().setMaximumBlockCount(1000)
  161. log_layout.addWidget(self.log_output)
  162. splitter.addWidget(log_widget)
  163. splitter.setSizes([500, 300])
  164. main_layout.addWidget(splitter)
  165. # 状态栏
  166. self.statusBar().showMessage("就绪")
  167. # 初始化触发记录
  168. self.triggered_records = {}
  169. def handle_login(self):
  170. username = self.username_input.text().strip()
  171. password = self.password_input.text().strip()
  172. if not username or not password:
  173. QMessageBox.warning(self, "警告", "请输入账号和密码")
  174. return
  175. self.login_btn.setEnabled(False)
  176. self.status_label.setText("登录中...")
  177. self.statusBar().showMessage("正在登录...")
  178. self.login_thread = LoginThread(username, password)
  179. self.login_thread.finished.connect(self.on_login_success)
  180. self.login_thread.error.connect(self.on_login_error)
  181. self.login_thread.start()
  182. def on_login_success(self, data):
  183. self.session_id = data.get('sessionId')
  184. self.username = data.get('userName', '')
  185. if self.session_id:
  186. self.login_btn.setEnabled(True)
  187. self.status_label.setText(f"已登录: {self.username}")
  188. self.manual_query_btn.setEnabled(True)
  189. self.start_btn.setEnabled(True)
  190. self.statusBar().showMessage(f"登录成功,Session ID: {self.session_id[:20]}...")
  191. # 登录成功后自动查询
  192. self.handle_query()
  193. else:
  194. self.on_login_error("登录响应中没有sessionId")
  195. def on_login_error(self, error_msg):
  196. self.login_btn.setEnabled(True)
  197. self.status_label.setText("登录失败")
  198. self.statusBar().showMessage("登录失败")
  199. QMessageBox.critical(self, "错误", error_msg)
  200. def handle_query(self):
  201. if not self.session_id:
  202. QMessageBox.warning(self, "警告", "请先登录")
  203. return
  204. self.manual_query_btn.setEnabled(False)
  205. self.statusBar().showMessage("正在查询车辆定位...")
  206. self.query_thread = QueryThread(self.session_id)
  207. self.query_thread.finished.connect(self.on_query_success)
  208. self.query_thread.error.connect(self.on_query_error)
  209. self.query_thread.start()
  210. def on_query_success(self, data):
  211. self.manual_query_btn.setEnabled(True)
  212. car_list = data.get('list', [])
  213. self.car_data = car_list
  214. # 保存当前勾选状态
  215. self.save_check_state()
  216. self.table.setRowCount(len(car_list))
  217. for row, car in enumerate(car_list):
  218. car_plate = car.get('carPlate', '')
  219. # 选择框 - 恢复保存的状态
  220. checkbox = QCheckBox()
  221. checkbox.setChecked(self.car_checked_state.get(car_plate, False))
  222. checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_check_changed(plate, state))
  223. self.table.setCellWidget(row, 0, checkbox)
  224. # 车队名
  225. team_name = car.get('teamName', '')
  226. self.table.setItem(row, 1, QTableWidgetItem(team_name))
  227. # 车牌号
  228. self.table.setItem(row, 2, QTableWidgetItem(car_plate))
  229. # 在线状态
  230. state_cn = car.get('stateCn', '')
  231. self.table.setItem(row, 3, QTableWidgetItem(state_cn))
  232. # 定位地址
  233. addr = car.get('addr', '')
  234. self.table.setItem(row, 4, QTableWidgetItem(addr))
  235. self.statusBar().showMessage(f"查询成功,共 {len(car_list)} 辆车")
  236. # 如果正在监控,进行地址匹配检查
  237. if self.is_monitoring:
  238. self.check_address_match()
  239. def on_query_error(self, error_msg):
  240. self.manual_query_btn.setEnabled(True)
  241. self.statusBar().showMessage("查询失败")
  242. QMessageBox.critical(self, "错误", error_msg)
  243. def save_check_state(self):
  244. """保存当前表格中的勾选状态"""
  245. for row in range(self.table.rowCount()):
  246. checkbox = self.table.cellWidget(row, 0)
  247. if checkbox:
  248. car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
  249. if car_plate:
  250. self.car_checked_state[car_plate] = checkbox.isChecked()
  251. def on_check_changed(self, car_plate, state):
  252. """勾选框状态变化时保存"""
  253. self.car_checked_state[car_plate] = (state == Qt.Checked)
  254. def select_all(self):
  255. for row in range(self.table.rowCount()):
  256. checkbox = self.table.cellWidget(row, 0)
  257. if checkbox:
  258. checkbox.setChecked(True)
  259. car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
  260. if car_plate:
  261. self.car_checked_state[car_plate] = True
  262. def deselect_all(self):
  263. for row in range(self.table.rowCount()):
  264. checkbox = self.table.cellWidget(row, 0)
  265. if checkbox:
  266. checkbox.setChecked(False)
  267. car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
  268. if car_plate:
  269. self.car_checked_state[car_plate] = False
  270. def start_monitoring(self):
  271. """启动监控"""
  272. load_addr = self.load_addr_input.text().strip()
  273. unload_addr = self.unload_addr_input.text().strip()
  274. if not load_addr and not unload_addr:
  275. QMessageBox.warning(self, "警告", "请至少输入一个地址关键词")
  276. return
  277. if not self.session_id:
  278. QMessageBox.warning(self, "警告", "请先登录")
  279. return
  280. self.is_monitoring = True
  281. self.start_btn.setEnabled(False)
  282. self.stop_btn.setEnabled(True)
  283. self.manual_query_btn.setEnabled(False)
  284. # 清空触发记录
  285. self.triggered_records = {}
  286. self.add_log("=" * 50)
  287. self.add_log(f"监控已启动 - 装车地址: {load_addr or '无'}, 卸车地址: {unload_addr or '无'}")
  288. self.add_log("=" * 50)
  289. # 立即执行一次检查
  290. self.handle_query()
  291. # 启动定时器,每5秒刷新一次
  292. self.refresh_timer.start(5000)
  293. self.statusBar().showMessage("监控运行中...")
  294. def stop_monitoring(self):
  295. """停止监控"""
  296. self.is_monitoring = False
  297. self.refresh_timer.stop()
  298. self.start_btn.setEnabled(True)
  299. self.stop_btn.setEnabled(False)
  300. self.manual_query_btn.setEnabled(True)
  301. self.add_log("=" * 50)
  302. self.add_log("监控已停止")
  303. self.add_log("=" * 50)
  304. self.statusBar().showMessage("监控已停止")
  305. def auto_refresh(self):
  306. """自动刷新"""
  307. if self.is_monitoring and self.session_id:
  308. self.handle_query()
  309. def check_address_match(self):
  310. """检查地址匹配"""
  311. load_keyword = self.load_addr_input.text().strip()
  312. unload_keyword = self.unload_addr_input.text().strip()
  313. current_time = datetime.now()
  314. for row in range(self.table.rowCount()):
  315. car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
  316. addr = self.table.item(row, 4).text() if self.table.item(row, 4) else ''
  317. if not car_plate or not addr:
  318. continue
  319. # 初始化该车辆的触发记录
  320. if car_plate not in self.triggered_records:
  321. self.triggered_records[car_plate] = {'load': None, 'unload': None}
  322. # 检查装车地址
  323. if load_keyword and load_keyword in addr:
  324. last_trigger = self.triggered_records[car_plate]['load']
  325. if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
  326. self.triggered_records[car_plate]['load'] = current_time
  327. self.add_log(f"🚛 {car_plate} 到达装车位置 ({load_keyword})")
  328. self.statusBar().showMessage(f"{car_plate} 到达装车位置")
  329. # 检查卸车地址
  330. if unload_keyword and unload_keyword in addr:
  331. last_trigger = self.triggered_records[car_plate]['unload']
  332. if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
  333. self.triggered_records[car_plate]['unload'] = current_time
  334. self.add_log(f"🚛 {car_plate} 到达卸车位置 ({unload_keyword})")
  335. self.statusBar().showMessage(f"{car_plate} 到达卸车位置")
  336. def add_log(self, message):
  337. """添加日志"""
  338. timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  339. log_line = f"[{timestamp}] {message}"
  340. self.log_output.append(log_line)
  341. # 保持最新日志可见
  342. scrollbar = self.log_output.verticalScrollBar()
  343. scrollbar.setValue(scrollbar.maximum())
  344. def main():
  345. app = QApplication(sys.argv)
  346. window = MainWindow()
  347. window.show()
  348. sys.exit(app.exec_())
  349. if __name__ == '__main__':
  350. main()