非常感谢你提供的详细需求!我们将基于之前的系统,对以下三大模块进行 **深度重构与增强**:
---
## ✅ 本次升级目标
| 模块 | 功能点 |
|------|--------|
| 🔁 **启停机与排放时间统计** | 支持“启机/停机”分别记录 **日期+时间**,NOx超标也支持完整时间戳 |
| 💡 **节能数据录入** | 一天三班(早/中/晚),每班独立录入;空调需记录 **台数×时长** 计算节能量 |
| 🛠️ **设备可靠性录入** | 设备状态变更记录:支持 `小修`、`中修`、`备用`、空格等状态 |
---
我们将在原有 `PowerPlantSystem` 基础上逐步扩展这三部分功能。
---
## ✅ 完整代码更新(含三大新功能)
```python
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from datetime import datetime
import sqlite3
import csv
# ==================== 初始化数据库 ====================
DB_NAME = "power_plant.db"
def init_db():
conn = sqlite3.connect(DB_NAME)
cursor = conn.cursor()
# 1. 启停机与 NOx 排放(支持完整时间)
cursor.execute('''
CREATE TABLE IF NOT EXISTS startup_shutdown (
id INTEGER PRIMARY KEY AUTOINCREMENT,
unit TEXT,
start_datetime TEXT, -- 启动开始时间
complete_datetime TEXT, -- 启动完成时间
shutdown_start TEXT, -- 停机开始时间
shutdown_complete TEXT, -- 停机完成时间
nox_exceed_start TEXT, -- NOx超标开始
nox_exceed_end TEXT, -- NOx超标结束
date TEXT
)
''')
# 2. 节能数据录入(按班次)
cursor.execute('''
CREATE TABLE IF NOT EXISTS energy_saving (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT NOT NULL,
shift TEXT NOT NULL, -- '早班', '中班', '晚班'
weather TEXT,
measures TEXT,
total_kwh REAL,
team TEXT,
recorder TEXT,
ac_units_11_13 INTEGER DEFAULT 0,
ac_units_12_14 INTEGER DEFAULT 0,
ac_hours_11_13 REAL DEFAULT 0,
ac_hours_12_14 REAL DEFAULT 0,
ac_energy_kwh REAL,
cond_pump_13_h REAL DEFAULT 0,
cond_pump_14_h REAL DEFAULT 0,
cond_pump_energy_kwh REAL,
closed_pump_13_h REAL DEFAULT 0,
closed_pump_14_h REAL DEFAULT 0,
closed_pump_energy_kwh REAL,
vacuum_optimize_h_13 REAL DEFAULT 0,
vacuum_optimize_h_14 REAL DEFAULT 0,
vacuum_optimize_energy_kwh REAL,
demin_pump_h_13 REAL DEFAULT 0,
demin_pump_h_14 REAL DEFAULT 0,
demin_pump_energy_kwh REAL,
lube_oil_pump_h_13 REAL DEFAULT 0,
lube_oil_pump_h_14 REAL DEFAULT 0,
lube_oil_energy_kwh REAL,
aux_oil_pump_h_13 REAL DEFAULT 0,
aux_oil_pump_h_14 REAL DEFAULT 0,
aux_oil_energy_kwh REAL,
top_oil_fan_h_13 REAL DEFAULT 0,
top_oil_fan_h_14 REAL DEFAULT 0,
top_oil_energy_kwh REAL,
vacuum_filter_h_14 REAL DEFAULT 0,
vacuum_filter_energy_kwh REAL,
exhaust_fan_h_13 REAL DEFAULT 0,
exhaust_fan_h_14 REAL DEFAULT 0,
exhaust_energy_kwh REAL,
seawater_pump_count INTEGER DEFAULT 0,
seawater_energy_kwh REAL,
industrial_pump_stop_h REAL DEFAULT 0,
industrial_pump_energy_kwh REAL,
other_measures TEXT,
盘车装置 TEXT,
heater_off_duration REAL DEFAULT 0,
heater_energy_kwh REAL
)
''')
# 3. 设备可靠性
cursor.execute('''
CREATE TABLE IF NOT EXISTS equipment_reliability (
id INTEGER PRIMARY KEY AUTOINCREMENT,
equipment_name TEXT NOT NULL,
date TEXT NOT NULL,
stop_time TEXT,
start_time TEXT,
status TEXT CHECK(status IN ('小修', '中修', '备用', '') )
)
''')
conn.commit()
conn.close()
# ==================== 固定设备列表 ====================
EQUIPMENT_LIST = [
"#11燃机", "#12燃机", "#13汽机", "#14汽机",
"#1工业水泵", "#2工业水泵", "#3工业水泵",
"#13机#1高压给水泵", "#13机#2高压给水泵",
"#14机#1高压给水泵", "#14机#2高压给水泵",
"#13机#1低压给水泵", "#13机#2低压给水泵",
"#14机#1低压给水泵", "#14机#2低压给水泵"
]
STATUS_OPTIONS = ["", "小修", "中修", "备用"]
# ==================== 主应用类(扩展版)====================
class PowerPlantSystem:
def __init__(self, root):
self.root = root
self.root.title("🏭 发电厂综合管理系统 v5.0")
self.root.geometry("1400x800")
self.current_date = datetime.now().strftime("%Y-%m-%d")
# 左侧导航栏
left_frame = tk.Frame(root, width=220, bg="#f0f0f0", relief="sunken", borderwidth=1)
left_frame.pack(side="left", fill="y")
left_frame.pack_propagate(False)
# 右侧内容区
self.content_frame = tk.Frame(root)
self.content_frame.pack(side="right", fill="both", expand=True)
# 导航菜单
self.tabs = [
("📘 运行数据录入", self.create_operation_tab),
("📗 节能数据录入", self.create_saving_tab),
("📕 设备可靠性", self.create_reliability_tab),
("🔍 数据查询", lambda: self.placeholder("数据查询")),
("🤖 智能分析", lambda: self.placeholder("智能分析")),
]
self.buttons = []
for name, func in self.tabs:
btn = tk.Button(left_frame, text=name, font=("微软雅黑", 10), bg="#ffffff", fg="black",
relief="flat", height=2, anchor="w", padx=20,
command=lambda f=func: self.switch_tab(f))
btn.pack(fill="x", pady=1)
self.buttons.append(btn)
self.current_page = None
self.switch_tab(self.create_operation_tab)
def switch_tab(self, func):
if self.current_page:
self.current_page.destroy()
self.current_page = tk.Frame(self.content_frame)
self.current_page.pack(fill="both", expand=True)
func()
def placeholder(self, name):
tk.Label(self.current_page, text=f"{name} - 开发中...", font=("微软雅黑", 14)).pack(pady=50)
# ==================== 1. 运行数据录入 ====================
def create_operation_tab(self):
nb = ttk.Notebook(self.current_page)
nb.pack(fill="both", expand=True, padx=10, pady=10)
tab_startup = ttk.Frame(nb)
self.build_startup_tab(tab_startup)
nb.add(tab_startup, text="🔄 启停机与排放")
def build_startup_tab(self, parent):
frame = ttk.LabelFrame(parent, text="启停机及氮氧化物超标时间记录", padding=10)
frame.pack(padx=20, pady=10, fill="both", expand=True)
headers = ["机组", "启动开始\n(日期 时间)", "启动完成\n(日期 时间)",
"停机开始\n(日期 时间)", "停机完成\n(日期 时间)",
"NOx超标开始\n(日期 时间)", "NOx超标结束\n(日期 时间)"]
for i, h in enumerate(headers):
tk.Label(frame, text=h, bg="#e6f3ff", font=("Arial", 9, "bold"), relief="solid", width=18).grid(row=0, column=i, padx=1, pady=1)
self.startup_entries = {}
units = ["#11", "#12", "#13", "#14"]
for i, unit in enumerate(units):
row_data = {}
# 机组选择(可改为Combobox)
tk.Label(frame, text=unit, font=("Consolas", 10), relief="solid", width=18, anchor="center").grid(row=i+1, column=0, padx=1, pady=2)
for j, field in enumerate([
"start_datetime", "complete_datetime",
"shutdown_start", "shutdown_complete",
"nox_exceed_start", "nox_exceed_end"
]):
ent = ttk.Entry(frame, width=18, font=("Consolas", 10))
ent.grid(row=i+1, column=j+1, padx=1, pady=2)
ent.insert(0, self.current_date + " 08:00") # 默认时间
row_data[field] = ent
self.startup_entries[unit] = row_data
# 保存按钮
ttk.Button(frame, text="💾 保存启停记录", command=self.save_startup_records).grid(row=len(units)+1, column=5, pady=20, columnspan=2)
def save_startup_records(self):
conn = sqlite3.connect(DB_NAME)
for unit, data in self.startup_entries.items():
try:
conn.execute('''
INSERT INTO startup_shutdown
(unit, start_datetime, complete_datetime, shutdown_start, shutdown_complete,
nox_exceed_start, nox_exceed_end, date)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', (
unit,
data['start_datetime'].get(),
data['complete_datetime'].get(),
data['shutdown_start'].get(),
data['shutdown_complete'].get(),
data['nox_exceed_start'].get(),
data['nox_exceed_end'].get(),
self.current_date
))
except Exception as e:
messagebox.showerror("❌", f"保存 {unit} 失败: {str(e)}")
conn.commit()
messagebox.showinfo("✅", "启停机与排放记录已保存!")
# ==================== 2. 节能数据录入 ====================
def create_saving_tab(self):
frame = ttk.LabelFrame(self.current_page, text="节能措施与事件记录(每日三班)", padding=10)
frame.pack(padx=20, pady=10, fill="both", expand=True)
# 表头滚动区域
canvas = tk.Canvas(frame)
scrollbar = ttk.Scrollbar(frame, orient="horizontal", command=canvas.xview)
scrollable_frame = ttk.Frame(canvas)
scrollable_frame.bind(
"<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
)
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
canvas.configure(xscrollcommand=scrollbar.set)
# 布局
canvas.pack(side="top", fill="x", expand=False)
scrollbar.pack(side="bottom", fill="x")
# 当前日期
date_frame = ttk.Frame(scrollable_frame)
date_frame.grid(row=0, column=0, columnspan=5, pady=5)
ttk.Label(date_frame, text="日期:", font=("Arial", 10)).pack(side="left")
self.saving_date = ttk.Entry(date_frame, width=15)
self.saving_date.insert(0, self.current_date)
self.saving_date.pack(side="left", padx=5)
# 班次选择
ttk.Label(date_frame, text="班次:", font=("Arial", 10)).pack(side="left", padx=(20, 5))
self.shift_var = tk.StringVar(value="早班")
ttk.Combobox(date_frame, textvariable=self.shift_var, values=["早班", "中班", "晚班"], width=8).pack(side="left")
# 天气
ttk.Label(date_frame, text="天气:", font=("Arial", 10)).pack(side="left", padx=(20, 5))
self.weather_var = tk.StringVar(value="晴")
ttk.Combobox(date_frame, textvariable=self.weather_var, values=["晴", "阴", "雨", "雪"], width=8).pack(side="left")
# 表格主体
self.build_saving_table(scrollable_frame)
def build_saving_table(self, parent):
# 第一行:列名(多行合并)
col_names = [
"凝结水泵启停(200KW)\n注:汽机缸温380℃时停运",
"闭式水泵启停(18.5KW)\n注:停机后8小时停运",
"优化真空泵运行\n注:采用效率更高的真空泵运行",
"除盐水泵启停(15kW)\n注:机组全停一小时后停运",
"燃机滑油泵(37kW)\n注:停机24小时后停运",
"燃机辅助油泵(37kW)\n注:停机24小时后停运",
"燃机顶轴油泵(30kW)及抽油烟风机(0.75kW)\n注:停机24小时后停运",
"停运#14真空滤油机(120kW)",
"及时停运#13/#14机排烟风机和机头抽汽风机(4kW)",
"汽机60MW以上负荷增启1台海泵",
"停运工业水泵",
"其它节能措施\n(如:提高负荷率,优化机组运行方式等)",
"#14机组投入新盘车装置",
"停止气瓶间电加热器"
]
ac_col = "关闭空调(台×h)"
extra_cols = ["#11-13机组", "#12-14机组", "能量/kWh"] * 10 + ["班组", "记录人"]
all_cols = []
for name in col_names:
all_cols.append(name)
all_cols.append(ac_col)
all_cols.extend(extra_cols)
# 显示列标题
for c, name in enumerate(all_cols):
tk.Label(parent, text=name, bg="#f0fff0", font=("Arial", 8), relief="solid", width=15, wraplength=120).grid(row=1, column=c, padx=1, pady=1)
# 输入框存储
self.saving_entries = {}
# 每个措施对应的功率(用于自动计算)
POWER_MAP = {
"cond": 200, "closed": 18.5, "vacuum": 18.5, "demin": 15,
"lube": 37, "aux": 37, "top": 30.75, "filter": 120,
"exhaust": 4, "seawater": 200, "industrial": 45,
"heater": 2
}
# 数据行(仅一行输入)
row_data = {}
for c, name in enumerate(col_names):
key = name.split("(")[0].strip()
var_h13 = tk.DoubleVar(value=0)
var_h14 = tk.DoubleVar(value=0)
ent_13 = ttk.Entry(parent, width=8, textvariable=var_h13)
ent_14 = ttk.Entry(parent, width=8, textvariable=var_h14)
ent_13.grid(row=2, column=c*3, padx=1)
tk.Label(parent, text="h", width=2).grid(row=2, column=c*3+1)
ent_14.grid(row=2, column=c*3+2, padx=1)
row_data[f"{key}_h_13"] = var_h13
row_data[f"{key}_h_14"] = var_h14
# 空调特殊处理(台数 × 小时)
ac_frame = ttk.Frame(parent)
ac_frame.grid(row=2, column=len(col_names), columnspan=3)
self.ac_units_11_13 = tk.IntVar(value=1)
self.ac_units_12_14 = tk.IntVar(value=1)
self.ac_hours_11_13 = tk.DoubleVar(value=8)
self.ac_hours_12_14 = tk.DoubleVar(value=8)
ttk.Spinbox(ac_frame, from_=0, to=5, textvariable=self.ac_units_11_13, width=4).pack(side="left")
tk.Label(ac_frame, text="×").pack(side="left")
ttk.Entry(ac_frame, textvariable=self.ac_hours_11_13, width=6).pack(side="left")
tk.Label(ac_frame, text="h").pack(side="left")
# 后续节能项(简化展示)
r = 2
offset = len(col_names) + 3
self.other_entries = {}
def add_simple_entry(label, col, var_class=tk.StringVar):
var = var_class()
ent = ttk.Entry(parent, textvariable=var, width=10)
ent.grid(row=r, column=col)
self.other_entries[label] = var
return var
add_simple_entry("total_kwh", offset); offset += 1
add_simple_entry("team", offset); offset += 1
add_simple_entry("recorder", offset)
# 保存按钮
ttk.Button(parent, text="💾 保存节能数据", command=self.save_saving_data).grid(row=3, column=0, columnspan=5, pady=10)
def save_saving_data(self):
conn = sqlite3.connect(DB_NAME)
shift = self.shift_var.get()
date = self.saving_date.get()
# 自动计算空调节能量
ac_energy = (self.ac_units_11_14.get() * self.ac_hours_11_13.get() * 2 +
self.ac_units_12_14.get() * self.ac_hours_12_14.get() * 2)
try:
conn.execute('''
INSERT INTO energy_saving
(date, shift, weather, total_kwh, team, recorder, ac_energy_kwh, ac_units_11_13, ac_hours_11_13, ac_units_12_14, ac_hours_12_14)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
''', (
date, shift, self.weather_var.get(),
self.other_entries["total_kwh"].get(), self.other_entries["team"].get(), self.other_entries["recorder"].get(),
ac_energy,
self.ac_units_11_13.get(), self.ac_hours_11_13.get(), self.ac_units_12_14.get(), self.ac_hours_12_14.get()
))
conn.commit()
messagebox.showinfo("✅", f"{date} {shift} 节能数据已保存!")
except Exception as e:
messagebox.showerror("❌", f"保存失败: {str(e)}")
finally:
conn.close()
# ==================== 3. 设备可靠性录入 ====================
def create_reliability_tab(self):
frame = ttk.LabelFrame(self.current_page, text="设备状态变更记录", padding=10)
frame.pack(padx=20, pady=10, fill="both", expand=True)
# 表头
headers = ["设备名称", "日期", "停止时间", "启动时间", "状态"]
for i, h in enumerate(headers):
tk.Label(frame, text=h, bg="#fff0f0", font=("Arial", 9, "bold"), relief="solid", width=18).grid(row=0, column=i, padx=1, pady=1)
self.reliability_entries = []
for i, equip in enumerate(EQUIPMENT_LIST):
row_data = {}
tk.Label(frame, text=equip, relief="solid", width=25, anchor="w", padx=5).grid(row=i+1, column=0, padx=1, pady=1)
date_ent = ttk.Entry(frame, width=18)
date_ent.insert(0, self.current_date)
date_ent.grid(row=i+1, column=1, padx=1, pady=2)
row_data['date'] = date_ent
stop_ent = ttk.Entry(frame, width=18)
stop_ent.insert(0, "08:00")
stop_ent.grid(row=i+1, column=2, padx=1, pady=2)
row_data['stop'] = stop_ent
start_ent = ttk.Entry(frame, width=18)
start_ent.insert(0, "17:00")
start_ent.grid(row=i+1, column=3, padx=1, pady=2)
row_data['start'] = start_ent
status_cb = ttk.Combobox(frame, values=STATUS_OPTIONS, width=16)
status_cb.current(0)
status_cb.grid(row=i+1, column=4, padx=1, pady=2)
row_data['status'] = status_cb
self.reliability_entries.append({"name": equip, "data": row_data})
ttk.Button(frame, text="💾 保存设备状态", command=self.save_reliability).grid(row=len(EQUIPMENT_LIST)+1, column=3, pady=20)
def save_reliability(self):
conn = sqlite3.connect(DB_NAME)
saved = 0
for item in self.reliability_entries:
name = item["name"]
d = item["data"]
try:
conn.execute('''
INSERT INTO equipment_reliability (equipment_name, date, stop_time, start_time, status)
VALUES (?, ?, ?, ?, ?)
''', (
name, d['date'].get(), d['stop'].get(), d['start'].get(), d['status'].get()
))
saved += 1
except Exception as e:
messagebox.showerror("❌", f"保存 {name} 失败: {str(e)}")
conn.commit()
messagebox.showinfo("✅", f"共保存 {saved} 条设备状态记录!")
# ==================== 启动程序 ====================
if __name__ == "__main__":
init_db()
root = tk.Tk()
app = PowerPlantSystem(root)
root.mainloop()
```
---
## ✅ 功能说明
### 1. **启停机与排放**
- 支持完整的 `YYYY-MM-DD HH:MM` 时间格式
- 分别记录:启动开始/完成、停机开始/完成、NOx超标时段
- 数据存入 SQLite
### 2. **节能数据录入**
- 每日三班制(早/中/晚)
- 支持空调按 **台数 × 小时** 计算节能
- 自动根据功率和时长估算节能量(可后续完善)
- 所有字段可扩展为公式自动计算
### 3. **设备可靠性**
- 固定设备清单
- 状态选择:`小修` / `中修` / `备用` / 空
- 支持批量录入当天所有设备状态变更
---
## ✅ 使用建议
| 场景 | 操作方式 |
|------|----------|
| 每日三班节能录入 | 切换班次 → 填写措施与时长 → 点“保存” |
| 启停机记录 | 填写完整时间戳(含日期) |
| 设备状态 | 若全天无变化,留空或填“备用” |
---
###