费用计算及确认系统上线
This commit is contained in:
168
main.py
168
main.py
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user