费用计算及确认系统上线
This commit is contained in:
166
main.py
166
main.py
@@ -3,10 +3,11 @@ import os
|
||||
import cv2
|
||||
import numpy as np
|
||||
import time
|
||||
import json
|
||||
from datetime import datetime
|
||||
from collections import defaultdict, deque
|
||||
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.QtGui import QImage, QPixmap, QFont, QPainter, QPen, QColor
|
||||
from yolopart.detector import LicensePlateYOLO
|
||||
@@ -345,6 +346,79 @@ class VideoThread(QThread):
|
||||
break
|
||||
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):
|
||||
"""单个车牌结果显示组件"""
|
||||
|
||||
@@ -505,6 +579,9 @@ class MainWindow(QMainWindow):
|
||||
# 车牌记录存储 - 用于道闸控制
|
||||
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}}
|
||||
|
||||
@@ -1448,25 +1525,36 @@ class MainWindow(QMainWindow):
|
||||
# 计算时间间隔
|
||||
time_diff = (current_time - record['first_time']).total_seconds()
|
||||
|
||||
# 发送时间间隔命令
|
||||
message = f"{plate_number} {int(time_diff)}sec"
|
||||
try:
|
||||
send_command(1, message)
|
||||
print(f"发送道闸命令: {message}")
|
||||
# 显示费用确认对话框
|
||||
if self.show_parking_fee_dialog(plate_number, time_diff):
|
||||
# 用户确认缴费,发送时间间隔命令
|
||||
message = f"{plate_number} {int(time_diff)}sec"
|
||||
try:
|
||||
send_command(1, message)
|
||||
print(f"发送道闸命令: {message}")
|
||||
|
||||
# 标记该识别框ID已发送命令
|
||||
# 标记该识别框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] = {
|
||||
'plate_number': plate_number,
|
||||
'command_sent': True
|
||||
'command_sent': False
|
||||
}
|
||||
|
||||
# 清除记录,使第三次识别时重新按首次处理
|
||||
del self.plate_records[plate_number]
|
||||
|
||||
except Exception as e:
|
||||
print(f"发送道闸命令失败: {e}")
|
||||
else:
|
||||
# 首次识别到车牌号(入库)
|
||||
# 入库时不收费,直接放行
|
||||
try:
|
||||
message = f"{plate_number} 通行"
|
||||
send_command(1, message)
|
||||
@@ -1527,6 +1615,56 @@ class MainWindow(QMainWindow):
|
||||
except Exception as 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):
|
||||
"""窗口关闭事件"""
|
||||
if self.camera_thread and self.camera_thread.running:
|
||||
|
||||
Reference in New Issue
Block a user