|
|
@@ -115,9 +115,25 @@ class WebSocketClient(QThread):
|
|
|
"""处理接收到的图片"""
|
|
|
try:
|
|
|
import base64
|
|
|
+ import json
|
|
|
+ import os
|
|
|
|
|
|
# 获取图片的base64编码
|
|
|
- img_base64 = data.get('value', '')
|
|
|
+ value = data.get('value', '{}')
|
|
|
+ print(f"收到原始value: {value}")
|
|
|
+
|
|
|
+ # 如果 value 已经是 dict,直接使用
|
|
|
+ if isinstance(value, dict):
|
|
|
+ img_base64 = value.get("base64", "")
|
|
|
+ plate = value.get("plate", "")
|
|
|
+ else:
|
|
|
+ # 如果是字符串,尝试解析 JSON
|
|
|
+ value_dict = json.loads(value)
|
|
|
+ img_base64 = value_dict.get("base64", "")
|
|
|
+ plate = value_dict.get("plate", "")
|
|
|
+
|
|
|
+ print(f"解析到车牌: {plate}")
|
|
|
+
|
|
|
if not img_base64:
|
|
|
self.error.emit("图片数据为空")
|
|
|
return
|
|
|
@@ -132,8 +148,22 @@ class WebSocketClient(QThread):
|
|
|
# 解码base64
|
|
|
image_data = base64.b64decode(img_base64)
|
|
|
|
|
|
- # 固定保存到C盘根目录
|
|
|
- filepath = "C:/imgIn.jpeg"
|
|
|
+ # 获取用户选择的保存目录
|
|
|
+ folder_path = self.get_folder_path()
|
|
|
+ if not folder_path:
|
|
|
+ self.error.emit("未选择文件夹,无法保存图片")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 确保目录存在
|
|
|
+ if not os.path.exists(folder_path):
|
|
|
+ try:
|
|
|
+ os.makedirs(folder_path)
|
|
|
+ except Exception as e:
|
|
|
+ self.error.emit(f"创建目录失败: {str(e)}")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 保存到用户选择的目录
|
|
|
+ filepath = os.path.join(folder_path, plate, "imgIn.jpeg")
|
|
|
|
|
|
# 保存图片
|
|
|
with open(filepath, 'wb') as f:
|
|
|
@@ -146,7 +176,8 @@ class WebSocketClient(QThread):
|
|
|
'type': 'imgSaved',
|
|
|
'filepath': filepath,
|
|
|
'filename': "imgIn.jpeg",
|
|
|
- 'sender_id': sender_id
|
|
|
+ 'sender_id': sender_id,
|
|
|
+ "plate": plate
|
|
|
}
|
|
|
self.message_received.emit(success_msg)
|
|
|
|
|
|
@@ -307,7 +338,7 @@ class MainWindow(QMainWindow):
|
|
|
self.wait_timeout_timer = QTimer() # 等待超时定时器
|
|
|
self.wait_timeout_timer.setSingleShot(True) # 单次触发
|
|
|
self.wait_timeout_timer.timeout.connect(self.on_wait_timeout)
|
|
|
-
|
|
|
+ self.car_task_state = {}
|
|
|
# WebSocket相关
|
|
|
self.ws_client = None
|
|
|
self.online_clients = {} # userID -> 客户端信息
|
|
|
@@ -464,9 +495,9 @@ class MainWindow(QMainWindow):
|
|
|
|
|
|
self.table = QTableWidget()
|
|
|
# 修改列数:增加"扫码"列和"操作"列
|
|
|
- self.table.setColumnCount(7)
|
|
|
- self.table.setHorizontalHeaderLabels(["启用", "扫码", "车队名", "车牌号", "状态", "定位地址", "操作"])
|
|
|
- self.table.horizontalHeader().setSectionResizeMode(5, QHeaderView.Stretch)
|
|
|
+ self.table.setColumnCount(6) # 减少一列
|
|
|
+ self.table.setHorizontalHeaderLabels(["启用", "车队名", "车牌号", "状态", "定位地址", "操作"])
|
|
|
+ self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
|
|
|
self.table.setAlternatingRowColors(True)
|
|
|
table_layout.addWidget(self.table)
|
|
|
|
|
|
@@ -477,14 +508,7 @@ class MainWindow(QMainWindow):
|
|
|
self.deselect_all_btn = QPushButton("取消全选启用")
|
|
|
self.deselect_all_btn.clicked.connect(self.deselect_all_enable)
|
|
|
btn_layout.addWidget(self.deselect_all_btn)
|
|
|
-
|
|
|
- self.select_all_scan_btn = QPushButton("全选扫码")
|
|
|
- self.select_all_scan_btn.clicked.connect(self.select_all_scan)
|
|
|
- btn_layout.addWidget(self.select_all_scan_btn)
|
|
|
- self.deselect_all_scan_btn = QPushButton("取消全选扫码")
|
|
|
- self.deselect_all_scan_btn.clicked.connect(self.deselect_all_scan)
|
|
|
- btn_layout.addWidget(self.deselect_all_scan_btn)
|
|
|
-
|
|
|
+
|
|
|
btn_layout.addStretch()
|
|
|
table_layout.addLayout(btn_layout)
|
|
|
|
|
|
@@ -597,7 +621,7 @@ class MainWindow(QMainWindow):
|
|
|
def on_ws_message(self, data):
|
|
|
"""处理WebSocket消息"""
|
|
|
msg_type = data.get('type')
|
|
|
- self.add_log(json.dumps(data, ensure_ascii=False))
|
|
|
+ print(json.dumps(data, ensure_ascii=False))
|
|
|
if msg_type == 'getList':
|
|
|
# 客户端列表
|
|
|
clients = data.get('value', [])
|
|
|
@@ -607,63 +631,72 @@ class MainWindow(QMainWindow):
|
|
|
elif msg_type == 'imgSaved':
|
|
|
# 图片保存成功,触发扫码任务
|
|
|
self.add_log(f"📸 图片已保存: {data.get('filepath', '')}")
|
|
|
- self.start_scan_tasks()
|
|
|
+ self.start_scan_tasks(data.get('plate', ''))
|
|
|
elif msg_type == 'drive': # 收到"开车"命令
|
|
|
- # 外部WebSocket发来的开车命令
|
|
|
car_plate = data.get('value', '')
|
|
|
if car_plate:
|
|
|
- # 检查是否是当前等待的车牌
|
|
|
- if car_plate == self.current_scan_plate:
|
|
|
- self.add_log(f"📤 收到外部WebSocket开车命令,车辆: {car_plate}")
|
|
|
- # 停止超时定时器
|
|
|
- self.wait_timeout_timer.stop()
|
|
|
- # 发送2001指令
|
|
|
- self.send_cmd_to_vehicle(car_plate, "2001")
|
|
|
- # 清空当前车牌
|
|
|
- self.current_scan_plate = None
|
|
|
- # 继续处理下一辆车
|
|
|
- self.process_next_scan()
|
|
|
- else:
|
|
|
- self.add_log(f"⚠️ 收到开车命令但车辆 {car_plate} 不在等待列表中(当前等待: {self.current_scan_plate})")
|
|
|
+ self.add_log(f"📤 收到外部WebSocket开车命令,车辆: {car_plate}")
|
|
|
+ # 设置状态为"已发车"
|
|
|
+ self.car_task_state[car_plate] = '已发车'
|
|
|
+ self.update_task_state_column()
|
|
|
+ self.add_log(f"📌 {car_plate} 状态更新为: 已发车")
|
|
|
+ # 停止超时定时器
|
|
|
+ self.wait_timeout_timer.stop()
|
|
|
+ # 发送2001指令
|
|
|
+ self.send_cmd_to_vehicle(car_plate, "2001")
|
|
|
+ # 清空当前车牌
|
|
|
+ self.current_scan_plate = None
|
|
|
+ # 继续处理下一辆车
|
|
|
+ self.process_next_scan()
|
|
|
else:
|
|
|
self.add_log(f"⚠️ 收到开车命令但车牌为空")
|
|
|
|
|
|
|
|
|
- def start_scan_tasks(self):
|
|
|
- """开始执行扫码任务"""
|
|
|
- # 如果正在处理,忽略新的请求
|
|
|
+ def start_scan_tasks(self, plate):
|
|
|
+ """开始执行扫码任务 - 只处理收到的车牌"""
|
|
|
if self.is_processing_scan:
|
|
|
self.add_log("⏳ 扫码任务正在处理中,忽略本次请求")
|
|
|
return
|
|
|
|
|
|
- # 获取所有勾选了扫码的车辆
|
|
|
- scan_vehicles = []
|
|
|
+ if not plate:
|
|
|
+ self.add_log("⚠️ 收到的车牌为空,跳过")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 检查"启用"复选框
|
|
|
+ if not self.car_checked_state.get(plate, False):
|
|
|
+ self.add_log(f"⚠️ 车辆 {plate} 未勾选启用,跳过")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 检查该车牌是否存在
|
|
|
+ car_exists = False
|
|
|
for row in range(self.table.rowCount()):
|
|
|
- scan_checkbox = self.table.cellWidget(row, 1)
|
|
|
- if scan_checkbox and scan_checkbox.isChecked():
|
|
|
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
|
|
|
- if car_plate:
|
|
|
- scan_vehicles.append(car_plate)
|
|
|
+ car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
|
|
|
+ if car_plate == plate:
|
|
|
+ car_exists = True
|
|
|
+ break
|
|
|
|
|
|
- if not scan_vehicles:
|
|
|
- self.add_log("⚠️ 没有勾选扫码的车辆")
|
|
|
+ if not car_exists:
|
|
|
+ self.add_log(f"⚠️ 车辆 {plate} 不在列表中,跳过")
|
|
|
return
|
|
|
|
|
|
- # 初始化队列
|
|
|
- self.scan_task_queue = scan_vehicles.copy()
|
|
|
+ # 设置状态为"等待扫码"
|
|
|
+ self.car_task_state[plate] = '等待扫码'
|
|
|
+ self.update_task_state_column()
|
|
|
+ self.add_log(f"📌 {plate} 状态更新为: 等待扫码")
|
|
|
+
|
|
|
+ # 初始化队列 - 只包含这一个车牌
|
|
|
+ self.scan_task_queue = [plate]
|
|
|
self.is_processing_scan = True
|
|
|
self.current_scan_plate = None
|
|
|
- self.wait_timeout_timer.stop() # 重置超时定时器
|
|
|
+ self.wait_timeout_timer.stop()
|
|
|
|
|
|
- self.add_log(f"🚀 开始扫码任务,共 {len(scan_vehicles)} 辆车")
|
|
|
- self.add_log(f"📋 车辆列表: {', '.join(scan_vehicles)}")
|
|
|
+ self.add_log(f"🚀 开始扫码任务,车辆: {plate}")
|
|
|
|
|
|
- # 立即处理第一个
|
|
|
+ # 立即处理第一个(也是唯一一个)
|
|
|
self.process_next_scan()
|
|
|
|
|
|
def process_next_scan(self):
|
|
|
"""处理下一个扫码任务"""
|
|
|
- # 如果监控已停止,不再处理
|
|
|
if not self.is_monitoring:
|
|
|
self.add_log("⏹️ 监控已停止,取消扫码任务")
|
|
|
self.is_processing_scan = False
|
|
|
@@ -674,7 +707,6 @@ class MainWindow(QMainWindow):
|
|
|
return
|
|
|
|
|
|
if not self.scan_task_queue:
|
|
|
- # 所有任务完成
|
|
|
self.is_processing_scan = False
|
|
|
self.add_log("✅ 所有扫码任务已完成")
|
|
|
self.statusBar().showMessage("扫码任务全部完成")
|
|
|
@@ -683,19 +715,15 @@ class MainWindow(QMainWindow):
|
|
|
self.wait_timeout_timer.stop()
|
|
|
return
|
|
|
|
|
|
- # 取出第一个车辆
|
|
|
car_plate = self.scan_task_queue.pop(0)
|
|
|
self.current_scan_plate = car_plate
|
|
|
self.add_log(f"🔄 正在处理扫码车辆: {car_plate} (剩余 {len(self.scan_task_queue)} 辆)")
|
|
|
|
|
|
- # 执行测试发车(标记为来自扫码任务)
|
|
|
self.on_test_drive(car_plate, from_scan=True)
|
|
|
|
|
|
- # 等待外部WebSocket通知发送2001指令
|
|
|
self.add_log(f"⏳ 等待外部WebSocket通知发送2001指令到: {car_plate},超时时间2分钟")
|
|
|
|
|
|
- # 启动超时定时器(2分钟)
|
|
|
- self.wait_timeout_timer.start(120000) # 2分钟 = 120000毫秒
|
|
|
+ self.wait_timeout_timer.start(120000)
|
|
|
|
|
|
def on_wait_timeout(self):
|
|
|
"""等待超时处理"""
|
|
|
@@ -705,17 +733,22 @@ class MainWindow(QMainWindow):
|
|
|
car_plate = self.current_scan_plate
|
|
|
self.add_log(f"⏰ 等待超时(2分钟),未收到 {car_plate} 的WebSocket通知")
|
|
|
|
|
|
- # 取消该车辆的扫码勾选
|
|
|
+ # 取消该车辆的启用勾选
|
|
|
for row in range(self.table.rowCount()):
|
|
|
- plate_item = self.table.item(row, 3)
|
|
|
+ plate_item = self.table.item(row, 2)
|
|
|
if plate_item and plate_item.text() == car_plate:
|
|
|
- scan_checkbox = self.table.cellWidget(row, 1)
|
|
|
- if scan_checkbox:
|
|
|
- scan_checkbox.setChecked(False)
|
|
|
- self.car_scan_state[car_plate] = False
|
|
|
- self.add_log(f"❌ 已取消 {car_plate} 的扫码勾选(超时)")
|
|
|
+ enable_checkbox = self.table.cellWidget(row, 0)
|
|
|
+ if enable_checkbox:
|
|
|
+ enable_checkbox.setChecked(False)
|
|
|
+ self.car_checked_state[car_plate] = False
|
|
|
+ self.add_log(f"❌ 已取消 {car_plate} 的启用勾选(超时)")
|
|
|
break
|
|
|
|
|
|
+ # 重置任务状态为"空闲"
|
|
|
+ self.car_task_state[car_plate] = '空闲'
|
|
|
+ self.update_task_state_column()
|
|
|
+ self.add_log(f"📌 {car_plate} 状态重置为: 空闲")
|
|
|
+
|
|
|
# 清空当前车牌
|
|
|
self.current_scan_plate = None
|
|
|
|
|
|
@@ -941,31 +974,49 @@ class MainWindow(QMainWindow):
|
|
|
enable_checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_enable_changed(plate, state))
|
|
|
self.table.setCellWidget(row, 0, enable_checkbox)
|
|
|
|
|
|
- # 第1列:扫码复选框
|
|
|
- scan_checkbox = QCheckBox()
|
|
|
- scan_checkbox.setChecked(self.car_scan_state.get(car_plate, False))
|
|
|
- scan_checkbox.stateChanged.connect(lambda state, plate=car_plate: self.on_scan_changed(plate, state))
|
|
|
- self.table.setCellWidget(row, 1, scan_checkbox)
|
|
|
-
|
|
|
- # 第2列:车队名
|
|
|
- self.table.setItem(row, 2, QTableWidgetItem(car.get('teamName', '')))
|
|
|
+ # 第1列:车队名
|
|
|
+ self.table.setItem(row, 1, QTableWidgetItem(car.get('teamName', '')))
|
|
|
|
|
|
- # 第3列:车牌号
|
|
|
- self.table.setItem(row, 3, QTableWidgetItem(car_plate))
|
|
|
+ # 第2列:车牌号
|
|
|
+ self.table.setItem(row, 2, QTableWidgetItem(car_plate))
|
|
|
|
|
|
- # 第4列:状态
|
|
|
- self.table.setItem(row, 4, QTableWidgetItem(car.get('stateCn', '')))
|
|
|
+ # 第3列:状态(车辆定位状态)
|
|
|
+ self.table.setItem(row, 3, QTableWidgetItem(car.get('stateCn', '')))
|
|
|
|
|
|
- # 第5列:定位地址
|
|
|
- self.table.setItem(row, 5, QTableWidgetItem(car.get('addr', '')))
|
|
|
+ # 第4列:定位地址
|
|
|
+ self.table.setItem(row, 4, QTableWidgetItem(car.get('addr', '')))
|
|
|
|
|
|
- # 第6列:操作按钮(测试发车)
|
|
|
+ # 第5列:操作按钮(测试发车)
|
|
|
test_btn = QPushButton("测试发车")
|
|
|
test_btn.clicked.connect(lambda checked, plate=car_plate: self.on_test_drive(plate))
|
|
|
- self.table.setCellWidget(row, 6, test_btn)
|
|
|
+ self.table.setCellWidget(row, 5, test_btn)
|
|
|
+
|
|
|
+ # 更新任务状态列(在表格后面添加一列显示任务状态)
|
|
|
+ self.update_task_state_column()
|
|
|
|
|
|
self.statusBar().showMessage(f"车辆列表更新: {len(car_list)} 辆车")
|
|
|
-
|
|
|
+
|
|
|
+ def update_task_state_column(self):
|
|
|
+ """更新任务状态列(在表格末尾添加)"""
|
|
|
+ # 检查是否已有任务状态列,如果没有则添加
|
|
|
+ if self.table.columnCount() < 7:
|
|
|
+ self.table.setColumnCount(7)
|
|
|
+ self.table.setHorizontalHeaderLabels(["启用", "车队名", "车牌号", "状态", "定位地址", "操作", "任务状态"])
|
|
|
+
|
|
|
+ for row in range(self.table.rowCount()):
|
|
|
+ car_plate = self.table.item(row, 2).text() if self.table.item(row, 2) else ''
|
|
|
+ if car_plate:
|
|
|
+ task_state = self.car_task_state.get(car_plate, '空闲')
|
|
|
+ state_item = QTableWidgetItem(task_state)
|
|
|
+ # 根据状态设置颜色
|
|
|
+ if task_state == '空闲':
|
|
|
+ state_item.setBackground(Qt.green)
|
|
|
+ elif task_state == '等待扫码':
|
|
|
+ state_item.setBackground(Qt.yellow)
|
|
|
+ elif task_state == '已发车':
|
|
|
+ state_item.setBackground(Qt.red)
|
|
|
+ self.table.setItem(row, 6, state_item)
|
|
|
+
|
|
|
def on_query_error(self, error_msg):
|
|
|
QMessageBox.critical(self, "错误", error_msg)
|
|
|
|
|
|
@@ -1015,25 +1066,7 @@ class MainWindow(QMainWindow):
|
|
|
if car_plate:
|
|
|
self.car_checked_state[car_plate] = False
|
|
|
|
|
|
- def select_all_scan(self):
|
|
|
- """全选扫码"""
|
|
|
- for row in range(self.table.rowCount()):
|
|
|
- checkbox = self.table.cellWidget(row, 1)
|
|
|
- if checkbox:
|
|
|
- checkbox.setChecked(True)
|
|
|
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
|
|
|
- if car_plate:
|
|
|
- self.car_scan_state[car_plate] = True
|
|
|
-
|
|
|
- def deselect_all_scan(self):
|
|
|
- """取消全选扫码"""
|
|
|
- for row in range(self.table.rowCount()):
|
|
|
- checkbox = self.table.cellWidget(row, 1)
|
|
|
- if checkbox:
|
|
|
- checkbox.setChecked(False)
|
|
|
- car_plate = self.table.item(row, 3).text() if self.table.item(row, 3) else ''
|
|
|
- if car_plate:
|
|
|
- self.car_scan_state[car_plate] = False
|
|
|
+
|
|
|
|
|
|
# ===== 测试发车功能 =====
|
|
|
def on_test_drive(self, car_plate, from_scan=False):
|
|
|
@@ -1212,7 +1245,7 @@ class MainWindow(QMainWindow):
|
|
|
"""处理区域检测结果"""
|
|
|
regions = data.get('list', [])
|
|
|
current_time = datetime.now()
|
|
|
- self.add_log(json.dumps(regions))
|
|
|
+ # self.add_log(json.dumps(regions))
|
|
|
for region in regions:
|
|
|
region_id = region.get('regionId')
|
|
|
region_name = region.get('regionName', '未知')
|
|
|
@@ -1236,23 +1269,20 @@ class MainWindow(QMainWindow):
|
|
|
if not car_id:
|
|
|
continue
|
|
|
|
|
|
- # 先初始化车牌
|
|
|
car_plate = None
|
|
|
car_state = None
|
|
|
- # 根据carId找车牌和状态
|
|
|
for car in self.car_data:
|
|
|
if str(car.get('carId')) == str(car_id):
|
|
|
car_plate = car.get('carPlate')
|
|
|
- car_state = car.get('stateCn', '') # 获取状态
|
|
|
+ car_state = car.get('stateCn', '')
|
|
|
break
|
|
|
|
|
|
- # 找不到车牌直接跳过
|
|
|
if not car_plate:
|
|
|
continue
|
|
|
- # 状态过滤:只处理 "在线-停车" 的车辆
|
|
|
+
|
|
|
if "在线-停车" not in car_state:
|
|
|
continue
|
|
|
- # 核心:启用过滤(必须放这里)
|
|
|
+
|
|
|
if not self.car_checked_state.get(car_plate, False):
|
|
|
continue
|
|
|
|
|
|
@@ -1265,8 +1295,15 @@ class MainWindow(QMainWindow):
|
|
|
if last_trigger is None or (current_time - last_trigger) >= timedelta(minutes=5):
|
|
|
self.triggered_records[car_plate][task_type] = current_time
|
|
|
task_name = "装车" if task_type == 'load' else "卸车"
|
|
|
+
|
|
|
+ # 设置状态为"等待扫码"
|
|
|
+ self.car_task_state[car_plate] = '等待扫码'
|
|
|
+ self.update_task_state_column()
|
|
|
+ self.add_log(f"📌 {car_plate} 状态更新为: 等待扫码 (检测到{task_name})")
|
|
|
+
|
|
|
self.add_log(f"🚛 {car_plate} 在 {region_name} 检测到{task_name}")
|
|
|
self.execute_task(car_plate, task_type)
|
|
|
+ break
|
|
|
|
|
|
# ===== 执行任务 =====
|
|
|
def execute_task(self, car_plate, task_type):
|