费用计算及确认系统上线

This commit is contained in:
2025-10-19 22:29:55 +08:00
parent a99e8fccb2
commit 418f7f3bc9

168
main.py
View File

@@ -3,10 +3,11 @@ import os
import cv2 import cv2
import numpy as np import numpy as np
import time import time
import json
from datetime import datetime from datetime import datetime
from collections import defaultdict, deque from collections import defaultdict, deque
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, \ from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, \
QFileDialog, QFrame, QScrollArea, QComboBox, QLineEdit, QListWidget, QListWidgetItem QFileDialog, QFrame, QScrollArea, QComboBox, QLineEdit, QListWidget, QListWidgetItem, QDialog, QMessageBox
from PyQt5.QtCore import QTimer, Qt, pyqtSignal, QThread from PyQt5.QtCore import QTimer, Qt, pyqtSignal, QThread
from PyQt5.QtGui import QImage, QPixmap, QFont, QPainter, QPen, QColor from PyQt5.QtGui import QImage, QPixmap, QFont, QPainter, QPen, QColor
from yolopart.detector import LicensePlateYOLO from yolopart.detector import LicensePlateYOLO
@@ -345,6 +346,79 @@ class VideoThread(QThread):
break break
self.msleep(30) # 约30fps self.msleep(30) # 约30fps
class ParkingFeeDialog(QDialog):
"""停车费用确认对话框"""
def __init__(self, plate_number, parking_duration, fee_amount, parent=None):
super().__init__(parent)
self.plate_number = plate_number
self.parking_duration = parking_duration
self.fee_amount = fee_amount
self.payment_confirmed = False
self.init_ui()
def init_ui(self):
self.setWindowTitle("停车费用确认")
self.setModal(True)
self.resize(400, 300)
layout = QVBoxLayout()
# 车牌号信息
plate_label = QLabel(f"车牌号: {self.plate_number}")
plate_label.setFont(QFont("Arial", 14, QFont.Bold))
layout.addWidget(plate_label)
# 停车时长信息
hours = self.parking_duration // 3600
minutes = (self.parking_duration % 3600) // 60
seconds = self.parking_duration % 60
duration_text = f"停车时长: {hours}小时{minutes}分钟{seconds}"
duration_label = QLabel(duration_text)
duration_label.setFont(QFont("Arial", 12))
layout.addWidget(duration_label)
# 费用信息
fee_label = QLabel(f"应缴费用: ¥{self.fee_amount:.2f}")
fee_label.setFont(QFont("Arial", 14, QFont.Bold))
fee_label.setStyleSheet("color: red;")
layout.addWidget(fee_label)
# 提示信息
tip_label = QLabel("请确认该车辆是否已经缴费:")
tip_label.setFont(QFont("Arial", 12))
layout.addWidget(tip_label)
# 按钮布局
button_layout = QHBoxLayout()
# 已缴费按钮
paid_button = QPushButton("已缴费 - 开闸放行")
paid_button.setFont(QFont("Arial", 12))
paid_button.setStyleSheet("QPushButton { background-color: #4CAF50; color: white; padding: 10px; }")
paid_button.clicked.connect(self.confirm_payment)
button_layout.addWidget(paid_button)
# 未缴费按钮
unpaid_button = QPushButton("未缴费 - 拒绝放行")
unpaid_button.setFont(QFont("Arial", 12))
unpaid_button.setStyleSheet("QPushButton { background-color: #f44336; color: white; padding: 10px; }")
unpaid_button.clicked.connect(self.reject_payment)
button_layout.addWidget(unpaid_button)
layout.addLayout(button_layout)
self.setLayout(layout)
def confirm_payment(self):
"""确认已缴费"""
self.payment_confirmed = True
self.accept()
def reject_payment(self):
"""拒绝放行"""
self.payment_confirmed = False
self.reject()
class LicensePlateWidget(QWidget): class LicensePlateWidget(QWidget):
"""单个车牌结果显示组件""" """单个车牌结果显示组件"""
@@ -505,6 +579,9 @@ class MainWindow(QMainWindow):
# 车牌记录存储 - 用于道闸控制 # 车牌记录存储 - 用于道闸控制
self.plate_records = {} # 格式: {plate_number: {'first_time': datetime, 'sent': bool}} self.plate_records = {} # 格式: {plate_number: {'first_time': datetime, 'sent': bool}}
# 加载停车费用配置
self.parking_config = self.load_parking_config()
# 识别框命令发送记录 - 防止同一识别框重复发送命令 # 识别框命令发送记录 - 防止同一识别框重复发送命令
self.frame_command_sent = {} # 格式: {plate_id: {'plate_number': str, 'command_sent': bool}} self.frame_command_sent = {} # 格式: {plate_id: {'plate_number': str, 'command_sent': bool}}
@@ -1448,25 +1525,36 @@ class MainWindow(QMainWindow):
# 计算时间间隔 # 计算时间间隔
time_diff = (current_time - record['first_time']).total_seconds() time_diff = (current_time - record['first_time']).total_seconds()
# 发送时间间隔命令 # 显示费用确认对话框
message = f"{plate_number} {int(time_diff)}sec" if self.show_parking_fee_dialog(plate_number, time_diff):
try: # 用户确认缴费,发送时间间隔命令
send_command(1, message) message = f"{plate_number} {int(time_diff)}sec"
print(f"发送道闸命令: {message}") try:
send_command(1, message)
# 标记该识别框ID已发送命令 print(f"发送道闸命令: {message}")
# 标记该识别框ID已发送命令
self.frame_command_sent[plate_id] = {
'plate_number': plate_number,
'command_sent': True
}
# 清除记录,使第三次识别时重新按首次处理
del self.plate_records[plate_number]
except Exception as e:
print(f"发送道闸命令失败: {e}")
else:
# 用户拒绝缴费或未缴费,不发送开闸命令
print(f"车牌 {plate_number} 未缴费,拒绝放行")
# 标记该识别框ID已处理避免重复弹窗
self.frame_command_sent[plate_id] = { self.frame_command_sent[plate_id] = {
'plate_number': plate_number, 'plate_number': plate_number,
'command_sent': True 'command_sent': False
} }
# 清除记录,使第三次识别时重新按首次处理
del self.plate_records[plate_number]
except Exception as e:
print(f"发送道闸命令失败: {e}")
else: else:
# 首次识别到车牌号(入库) # 首次识别到车牌号(入库)
# 入库时不收费,直接放行
try: try:
message = f"{plate_number} 通行" message = f"{plate_number} 通行"
send_command(1, message) send_command(1, message)
@@ -1527,6 +1615,56 @@ class MainWindow(QMainWindow):
except Exception as e: except Exception as e:
print(f"发送关闸命令失败: {e}") print(f"发送关闸命令失败: {e}")
def load_parking_config(self):
"""加载停车费用配置"""
try:
config_path = os.path.join(os.path.dirname(__file__), 'parking_config.json')
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
print(f"停车费用配置加载成功: {config}")
return config
except Exception as e:
print(f"加载停车费用配置失败: {e}")
# 返回默认配置
return {
"free_parking_duration": 1800, # 30分钟免费停车
"billing_cycle": 3600, # 1小时计费周期
"price_per_cycle": 5.0 # 每小时5元
}
def calculate_parking_fee(self, parking_duration):
"""计算停车费用"""
free_duration = self.parking_config.get("free_parking_duration", 1800)
billing_cycle = self.parking_config.get("billing_cycle", 3600)
price_per_cycle = self.parking_config.get("price_per_cycle", 5.0)
# 如果停车时长在免费时间内费用为0
if parking_duration <= free_duration:
return 0.0
# 计算超出免费时间的部分
chargeable_duration = parking_duration - free_duration
# 计算需要收费的周期数(向上取整)
cycles = (chargeable_duration + billing_cycle - 1) // billing_cycle
# 计算总费用
total_fee = cycles * price_per_cycle
return total_fee
def show_parking_fee_dialog(self, plate_number, parking_duration):
"""显示停车费用确认对话框"""
fee_amount = self.calculate_parking_fee(parking_duration)
dialog = ParkingFeeDialog(plate_number, parking_duration, fee_amount, self)
result = dialog.exec_()
if result == QDialog.Accepted and dialog.payment_confirmed:
return True # 确认已缴费,允许开闸
else:
return False # 未缴费或取消,拒绝开闸
def closeEvent(self, event): def closeEvent(self, event):
"""窗口关闭事件""" """窗口关闭事件"""
if self.camera_thread and self.camera_thread.running: if self.camera_thread and self.camera_thread.running: