| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- import sys
- import hashlib
- import requests
- import json
- import time
- from datetime import datetime, timedelta
- from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
- QHBoxLayout, QLabel, QLineEdit, QPushButton,
- QTableWidget, QTableWidgetItem, QHeaderView,
- QCheckBox, QMessageBox, QGroupBox, QTextEdit,
- QGridLayout, QSplitter)
- from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
- from PyQt5.QtGui import QTextDocument
- class LoginThread(QThread):
- """登录线程"""
- finished = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, username, password):
- super().__init__()
- self.username = username
- self.password = password
-
- def run(self):
- try:
- md5_password = hashlib.md5(self.password.encode('utf-8')).hexdigest()
- 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"
-
- response = requests.get(url, timeout=10)
- response.encoding = 'utf-8'
-
- if response.status_code == 200:
- data = response.json()
- if data.get('rspCode') == '1':
- self.finished.emit(data)
- else:
- self.error.emit(f"登录失败: {data.get('msg', '未知错误')}")
- else:
- self.error.emit(f"请求失败: HTTP {response.status_code}")
- except Exception as e:
- self.error.emit(f"登录异常: {str(e)}")
- class QueryThread(QThread):
- """查询车辆定位线程"""
- finished = pyqtSignal(dict)
- error = pyqtSignal(str)
-
- def __init__(self, session_id):
- super().__init__()
- self.session_id = session_id
-
- def run(self):
- try:
- url = f"http://119.91.58.175:9999/gps-web/api/get_gps_r.jsp?sessionId={self.session_id}&teamId=&carIds=&simple=false"
-
- response = requests.get(url, timeout=15)
- response.encoding = 'utf-8'
-
- if response.status_code == 200:
- data = response.json()
- if data.get('rspCode') == '1':
- self.finished.emit(data)
- else:
- self.error.emit(f"查询失败: {data.get('msg', '未知错误')}")
- else:
- self.error.emit(f"请求失败: HTTP {response.status_code}")
- except Exception as e:
- self.error.emit(f"查询异常: {str(e)}")
- class MainWindow(QMainWindow):
- def __init__(self):
- super().__init__()
- self.session_id = None
- self.username = None
- self.car_data = []
- self.car_checked_state = {} # 存储每辆车的勾选状态
- self.triggered_records = {} # 存储触发记录 {car_plate: {'load': last_time, 'unload': last_time}}
- self.is_monitoring = False
- self.refresh_timer = QTimer()
- self.refresh_timer.timeout.connect(self.auto_refresh)
- self.initUI()
-
- def initUI(self):
- self.setWindowTitle('车辆定位监控系统')
- self.setGeometry(100, 100, 1400, 800)
-
- # 中央窗口
- central_widget = QWidget()
- self.setCentralWidget(central_widget)
- main_layout = QVBoxLayout(central_widget)
-
- # 登录区域
- login_group = QGroupBox("登录")
- login_layout = QHBoxLayout()
-
- login_layout.addWidget(QLabel("账号:"))
- self.username_input = QLineEdit()
- self.username_input.setPlaceholderText("请输入账号")
- self.username_input.setText("深金川")
- login_layout.addWidget(self.username_input)
-
- login_layout.addWidget(QLabel("密码:"))
- self.password_input = QLineEdit()
- self.password_input.setPlaceholderText("请输入密码")
- self.password_input.setEchoMode(QLineEdit.Password)
- self.password_input.setText("123456")
- login_layout.addWidget(self.password_input)
-
- self.login_btn = QPushButton("登录")
- self.login_btn.clicked.connect(self.handle_login)
- login_layout.addWidget(self.login_btn)
-
- self.status_label = QLabel("未登录")
- login_layout.addWidget(self.status_label)
-
- login_layout.addStretch()
- login_group.setLayout(login_layout)
- main_layout.addWidget(login_group)
-
- # 地址输入区域
- addr_group = QGroupBox("地址监控设置")
- addr_layout = QGridLayout()
-
- addr_layout.addWidget(QLabel("装车地址:"), 0, 0)
- self.load_addr_input = QLineEdit()
- self.load_addr_input.setPlaceholderText("请输入装车地址关键词")
- addr_layout.addWidget(self.load_addr_input, 0, 1)
-
- addr_layout.addWidget(QLabel("卸车地址:"), 0, 2)
- self.unload_addr_input = QLineEdit()
- self.unload_addr_input.setPlaceholderText("请输入卸车地址关键词")
- addr_layout.addWidget(self.unload_addr_input, 0, 3)
-
- # 控制按钮
- self.start_btn = QPushButton("启动监控")
- self.start_btn.clicked.connect(self.start_monitoring)
- self.start_btn.setEnabled(False)
- addr_layout.addWidget(self.start_btn, 0, 4)
-
- self.stop_btn = QPushButton("停止监控")
- self.stop_btn.clicked.connect(self.stop_monitoring)
- self.stop_btn.setEnabled(False)
- addr_layout.addWidget(self.stop_btn, 0, 5)
-
- self.manual_query_btn = QPushButton("手动刷新")
- self.manual_query_btn.clicked.connect(self.handle_query)
- self.manual_query_btn.setEnabled(False)
- addr_layout.addWidget(self.manual_query_btn, 0, 6)
-
- addr_layout.addWidget(QLabel("刷新间隔:5秒"), 0, 7)
-
- addr_group.setLayout(addr_layout)
- main_layout.addWidget(addr_group)
-
- # 分割器:表格和日志
- splitter = QSplitter(Qt.Vertical)
-
- # 车辆信息表格
- table_widget = QWidget()
- table_layout = QVBoxLayout(table_widget)
- table_layout.setContentsMargins(0, 0, 0, 0)
-
- self.table = QTableWidget()
- self.table.setColumnCount(5)
- self.table.setHorizontalHeaderLabels(["选择", "车队名", "车牌号", "在线状态", "定位地址"])
- self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
- self.table.setAlternatingRowColors(True)
- table_layout.addWidget(self.table)
-
- # 表格按钮
- table_btn_layout = QHBoxLayout()
- self.select_all_btn = QPushButton("全选")
- self.select_all_btn.clicked.connect(self.select_all)
- table_btn_layout.addWidget(self.select_all_btn)
-
- self.deselect_all_btn = QPushButton("取消全选")
- self.deselect_all_btn.clicked.connect(self.deselect_all)
- table_btn_layout.addWidget(self.deselect_all_btn)
-
- table_btn_layout.addStretch()
- table_layout.addLayout(table_btn_layout)
-
- splitter.addWidget(table_widget)
-
- # 日志输出框
- log_widget = QWidget()
- log_layout = QVBoxLayout(log_widget)
- log_layout.setContentsMargins(0, 0, 0, 0)
-
- log_label = QLabel("日志输出")
- log_layout.addWidget(log_label)
-
- self.log_output = QTextEdit()
- self.log_output.setReadOnly(True)
- # 限制日志行数 - 使用document().setMaximumBlockCount()
- self.log_output.document().setMaximumBlockCount(1000)
- log_layout.addWidget(self.log_output)
-
- splitter.addWidget(log_widget)
- splitter.setSizes([500, 300])
-
- main_layout.addWidget(splitter)
-
- # 状态栏
- self.statusBar().showMessage("就绪")
-
- # 初始化触发记录
- self.triggered_records = {}
-
- def handle_login(self):
- username = self.username_input.text().strip()
- password = self.password_input.text().strip()
-
- if not username or not password:
- QMessageBox.warning(self, "警告", "请输入账号和密码")
- return
-
- self.login_btn.setEnabled(False)
- self.status_label.setText("登录中...")
- self.statusBar().showMessage("正在登录...")
-
- self.login_thread = LoginThread(username, password)
- self.login_thread.finished.connect(self.on_login_success)
- self.login_thread.error.connect(self.on_login_error)
- self.login_thread.start()
-
- def on_login_success(self, data):
- self.session_id = data.get('sessionId')
- self.username = data.get('userName', '')
-
- if self.session_id:
- self.login_btn.setEnabled(True)
- self.status_label.setText(f"已登录: {self.username}")
- self.manual_query_btn.setEnabled(True)
- self.start_btn.setEnabled(True)
- self.statusBar().showMessage(f"登录成功,Session ID: {self.session_id[:20]}...")
-
- # 登录成功后自动查询
- self.handle_query()
- else:
- self.on_login_error("登录响应中没有sessionId")
-
- def on_login_error(self, error_msg):
- self.login_btn.setEnabled(True)
- self.status_label.setText("登录失败")
- self.statusBar().showMessage("登录失败")
- QMessageBox.critical(self, "错误", error_msg)
-
- def handle_query(self):
- if not self.session_id:
- QMessageBox.warning(self, "警告", "请先登录")
- return
-
- self.manual_query_btn.setEnabled(False)
- self.statusBar().showMessage("正在查询车辆定位...")
-
- self.query_thread = QueryThread(self.session_id)
- self.query_thread.finished.connect(self.on_query_success)
- self.query_thread.error.connect(self.on_query_error)
- self.query_thread.start()
-
- def on_query_success(self, data):
- self.manual_query_btn.setEnabled(True)
-
- car_list = data.get('list', [])
- self.car_data = car_list
-
- # 保存当前勾选状态
- self.save_check_state()
-
- self.table.setRowCount(len(car_list))
-
- for row, car in enumerate(car_list):
- car_plate = car.get('carPlate', '')
-
- # 选择框 - 恢复保存的状态
- checkbox = QCheckBox()
- checkbox.setChecked(self.car_checked_state.get(car_plate, False))
- checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_check_changed(plate, state))
- self.table.setCellWidget(row, 0, checkbox)
-
- # 车队名
- team_name = car.get('teamName', '')
- self.table.setItem(row, 1, QTableWidgetItem(team_name))
-
- # 车牌号
- self.table.setItem(row, 2, QTableWidgetItem(car_plate))
-
- # 在线状态
- state_cn = car.get('stateCn', '')
- self.table.setItem(row, 3, QTableWidgetItem(state_cn))
-
- # 定位地址
- addr = car.get('addr', '')
- self.table.setItem(row, 4, QTableWidgetItem(addr))
-
- self.statusBar().showMessage(f"查询成功,共 {len(car_list)} 辆车")
-
- # 如果正在监控,进行地址匹配检查
- if self.is_monitoring:
- self.check_address_match()
-
- def on_query_error(self, error_msg):
- self.manual_query_btn.setEnabled(True)
- self.statusBar().showMessage("查询失败")
- QMessageBox.critical(self, "错误", error_msg)
-
- def save_check_state(self):
- """保存当前表格中的勾选状态"""
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 0)
- if checkbox:
- car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
- if car_plate:
- self.car_checked_state[car_plate] = checkbox.isChecked()
-
- def on_check_changed(self, car_plate, state):
- """勾选框状态变化时保存"""
- self.car_checked_state[car_plate] = (state == Qt.Checked)
-
- def select_all(self):
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 0)
- if checkbox:
- checkbox.setChecked(True)
- car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
- if car_plate:
- self.car_checked_state[car_plate] = True
-
- def deselect_all(self):
- for row in range(self.table.rowCount()):
- checkbox = self.table.cellWidget(row, 0)
- if checkbox:
- checkbox.setChecked(False)
- car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
- if car_plate:
- self.car_checked_state[car_plate] = False
-
- def start_monitoring(self):
- """启动监控"""
- load_addr = self.load_addr_input.text().strip()
- unload_addr = self.unload_addr_input.text().strip()
-
- if not load_addr and not unload_addr:
- QMessageBox.warning(self, "警告", "请至少输入一个地址关键词")
- return
-
- if not self.session_id:
- QMessageBox.warning(self, "警告", "请先登录")
- return
-
- self.is_monitoring = True
- self.start_btn.setEnabled(False)
- self.stop_btn.setEnabled(True)
- self.manual_query_btn.setEnabled(False)
-
- # 清空触发记录
- self.triggered_records = {}
-
- self.add_log("=" * 50)
- self.add_log(f"监控已启动 - 装车地址: {load_addr or '无'}, 卸车地址: {unload_addr or '无'}")
- self.add_log("=" * 50)
-
- # 立即执行一次检查
- self.handle_query()
-
- # 启动定时器,每5秒刷新一次
- self.refresh_timer.start(5000)
- self.statusBar().showMessage("监控运行中...")
-
- def stop_monitoring(self):
- """停止监控"""
- self.is_monitoring = False
- self.refresh_timer.stop()
- self.start_btn.setEnabled(True)
- self.stop_btn.setEnabled(False)
- self.manual_query_btn.setEnabled(True)
-
- self.add_log("=" * 50)
- self.add_log("监控已停止")
- self.add_log("=" * 50)
- self.statusBar().showMessage("监控已停止")
-
- def auto_refresh(self):
- """自动刷新"""
- if self.is_monitoring and self.session_id:
- self.handle_query()
-
- def check_address_match(self):
- """检查地址匹配"""
- load_keyword = self.load_addr_input.text().strip()
- unload_keyword = self.unload_addr_input.text().strip()
- current_time = datetime.now()
-
- for row in range(self.table.rowCount()):
- car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
- addr = self.table.item(row, 4).text() if self.table.item(row, 4) else ''
-
- if not car_plate or not addr:
- continue
-
- # 初始化该车辆的触发记录
- if car_plate not in self.triggered_records:
- self.triggered_records[car_plate] = {'load': None, 'unload': None}
-
- # 检查装车地址
- if load_keyword and load_keyword in addr:
- last_trigger = self.triggered_records[car_plate]['load']
- if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
- self.triggered_records[car_plate]['load'] = current_time
- self.add_log(f"🚛 {car_plate} 到达装车位置 ({load_keyword})")
- self.statusBar().showMessage(f"{car_plate} 到达装车位置")
-
- # 检查卸车地址
- if unload_keyword and unload_keyword in addr:
- last_trigger = self.triggered_records[car_plate]['unload']
- if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
- self.triggered_records[car_plate]['unload'] = current_time
- self.add_log(f"🚛 {car_plate} 到达卸车位置 ({unload_keyword})")
- self.statusBar().showMessage(f"{car_plate} 到达卸车位置")
-
- def add_log(self, message):
- """添加日志"""
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
- log_line = f"[{timestamp}] {message}"
- self.log_output.append(log_line)
-
- # 保持最新日志可见
- scrollbar = self.log_output.verticalScrollBar()
- scrollbar.setValue(scrollbar.maximum())
- def main():
- app = QApplication(sys.argv)
- window = MainWindow()
- window.show()
- sys.exit(app.exec_())
- if __name__ == '__main__':
- main()
|