Reorganize display with side-by-side nail panels for more header space

- Moved NAIL 1 and NAIL 2 panels to side-by-side layout
- Created compact panel format with essential metrics only
- Freed up vertical space for larger ASCII art header
- New header shows 'piNail' in larger ASCII art
- More efficient use of screen space and cleaner layout
- Deployed and verified on Pi
This commit is contained in:
2026-03-12 04:18:26 +00:00
parent 7742f15912
commit 6870937c7c
+58 -59
View File
@@ -148,79 +148,69 @@ class StatusDisplay:
color, label = phases.get(phase, (Colors.WHITE, " ? ")) color, label = phases.get(phase, (Colors.WHITE, " ? "))
return "{}{}{}".format(color, label, Colors.RESET) return "{}{}{}".format(color, label, Colors.RESET)
def draw_nail_panel(self, nail_id, status): def draw_nail_panel_compact(self, nail_id, status):
"""Draw status panel for a single nail.""" """Draw compact status panel for a single nail (for side-by-side layout)."""
if not status: if not status:
return [] return []
lines = [] lines = []
# Header # Header
nail_name = "NAIL 1" if nail_id == "nail1" else "NAIL 2" nail_name = "N1" if nail_id == "nail1" else "N2"
enabled = status.get("enabled", False) enabled = status.get("enabled", False)
has_error = status.get("error", False) has_error = status.get("error", False)
icon = self.format_status_icon(enabled, has_error) icon = self.format_status_icon(enabled, has_error)
lines.append("{}{} {} {}{}".format( lines.append("{}{}{}{}".format(
Colors.BOLD, nail_name, icon, Colors.BOLD, nail_name, icon,
"ONLINE" if enabled else "OFFLINE", "ON" if enabled else "OFF"
Colors.RESET ) + Colors.RESET)
))
# Temperature display # Temperature display
current = float(status.get("current_temp", 0)) current = float(status.get("current_temp", 0))
setpoint = float(status.get("setpoint", 0)) setpoint = float(status.get("setpoint", 0))
lines.append(" Temp: {} / {}{}C".format( lines.append("T:{} S:{}".format(
self.format_temp(current), "{:5.0f}F".format(current) if current else "ERR",
self.format_temp(setpoint), "{:5.0f}F".format(setpoint)
Colors.RESET
)) ))
# Error display # Error display
error = float(status.get("error", 0)) error = float(status.get("error", 0))
error_color = Colors.RED if abs(error) > 20 else Colors.YELLOW if abs(error) > 5 else Colors.GREEN error_color = Colors.RED if abs(error) > 20 else Colors.YELLOW if abs(error) > 5 else Colors.GREEN
lines.append(" Error: {}{:+7.1f}F{}".format(error_color, error, Colors.RESET)) lines.append("{}E:{:+6.1f}{}".format(error_color, error, Colors.RESET))
# PID Output # PID Output bar
output = float(status.get("output", 0)) output = float(status.get("output", 0))
output_pct = min(100.0, max(0.0, output * 100)) output_pct = min(100.0, max(0.0, output * 100))
bar_filled = int(output_pct / 5) bar_filled = int(output_pct / 10)
bar = "{}|{}{}|{}".format( bar = "{}|{}{}|{}".format(
Colors.BRIGHT_GREEN, Colors.BRIGHT_GREEN,
"=" * bar_filled, "=" * bar_filled,
" " * (20 - bar_filled), " " * (10 - bar_filled),
Colors.RESET Colors.RESET
) )
lines.append(" Output: {} {:5.1f}%".format(bar, output_pct)) lines.append("Out: {} {:3.0f}%".format(bar, output_pct))
# Flight mode and phase # Flight mode and phase
mode = status.get("flight_mode", "grounded") mode = status.get("flight_mode", "grounded")
phase = status.get("phase", "idle") phase = status.get("phase", "idle")
lines.append(" Mode: {} Phase: {}".format( mode_short = mode[:4].upper()
self.format_mode_badge(mode), phase_short = phase[:4].upper()
self.format_phase_badge(phase) lines.append("{}M:{} P:{}{}".format(
Colors.CYAN,
mode_short,
phase_short,
Colors.RESET
)) ))
# PID coefficients
pid = status.get("pid", {})
kp = float(pid.get("kP", 0))
ki = float(pid.get("kI", 0))
kd = float(pid.get("kD", 0))
lines.append(" PID: kP={:6.2f} kI={:6.2f} kD={:6.2f}".format(kp, ki, kd))
# Integral and derivative
integral = float(pid.get("integral", 0))
derivative = float(pid.get("derivative", 0))
lines.append(" Err-I: {:8.1f} Err-D: {:8.3f}".format(integral, derivative))
# Safety status # Safety status
safety = status.get("safety", {}) safety = status.get("safety", {})
temp_ok = safety.get("temp_ok", True) temp_ok = safety.get("temp_ok", True)
tc_ok = safety.get("tc_ok", True) tc_ok = safety.get("tc_ok", True)
watchdog_ok = safety.get("watchdog_ok", True) watchdog_ok = safety.get("watchdog_ok", True)
safety_status = "{}OK{}".format(Colors.GREEN, Colors.RESET) if (temp_ok and tc_ok and watchdog_ok) else "{}WARN{}".format(Colors.RED, Colors.RESET) safety_status = "{}S:OK{}".format(Colors.GREEN, Colors.RESET) if (temp_ok and tc_ok and watchdog_ok) else "{}S:WARN{}".format(Colors.RED, Colors.RESET)
lines.append(" Safety: {}".format(safety_status)) lines.append(safety_status)
return lines return lines
@@ -231,25 +221,33 @@ class StatusDisplay:
# ASCII Art Title with timestamp side-by-side # ASCII Art Title with timestamp side-by-side
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
lines.append("") lines.append("")
lines.append("{}{}{:<20} {}{}{}".format( lines.append("{}{}{:<22} {}{}{}".format(
Colors.BRIGHT_CYAN + Colors.BOLD, Colors.BRIGHT_CYAN + Colors.BOLD,
" ___ _ _ _ _", " ___ __ _ _ _ _",
"", "",
Colors.DIM, Colors.DIM,
timestamp, timestamp,
Colors.RESET Colors.RESET
)) ))
lines.append("{}{}{:<20} {}{}{}".format( lines.append("{}{}{:<22} {}{}{}".format(
Colors.BRIGHT_CYAN + Colors.BOLD, Colors.BRIGHT_CYAN + Colors.BOLD,
"| \\| | __ _ _ _| | ||_|", " / _ \\ / / | \\| | | | |",
"", "",
Colors.DIM, Colors.DIM,
"piNail2 Status", "piNail2 e-Nail",
Colors.RESET Colors.RESET
)) ))
lines.append("{}{}{:<20}{}".format( lines.append("{}{}{:<22} {}{}{}".format(
Colors.BRIGHT_CYAN + Colors.BOLD, Colors.BRIGHT_CYAN + Colors.BOLD,
"|__/|_|__|||_||_||_|\\__/", "| |_| |/ /__ \\ / |_|_|",
"",
Colors.DIM,
"Temperature Controller",
Colors.RESET
))
lines.append("{}{}{:<22}{}".format(
Colors.BRIGHT_CYAN + Colors.BOLD,
" \\___/______| \\/ ",
"", "",
Colors.RESET Colors.RESET
)) ))
@@ -261,29 +259,30 @@ class StatusDisplay:
nail1_data = self.data.get("nail1", {}) nail1_data = self.data.get("nail1", {})
nail2_data = self.data.get("nail2", {}) nail2_data = self.data.get("nail2", {})
# Nail 1 panel # Get compact panel lines for both nails
lines.append("{}┌─ NAIL 1 {}".format(Colors.CYAN, Colors.RESET)) nail1_lines = self.draw_nail_panel_compact("nail1", nail1_data)
lines.append("") nail2_lines = self.draw_nail_panel_compact("nail2", nail2_data)
for line in self.draw_nail_panel("nail1", nail1_data):
lines.append("{}".format(line))
lines.append("")
# Separator # Pad lines to same length
lines.append("") max_lines = max(len(nail1_lines), len(nail2_lines))
while len(nail1_lines) < max_lines:
nail1_lines.append("")
while len(nail2_lines) < max_lines:
nail2_lines.append("")
# Nail 2 panel # Draw side-by-side panels
lines.append("{}┌─ NAIL 2 {}".format(Colors.CYAN, Colors.RESET)) lines.append("{}┌─ NAIL 1 ────────────┬─ NAIL 2 ────────────┐{}".format(
lines.append("") Colors.CYAN, Colors.RESET))
for line in self.draw_nail_panel("nail2", nail2_data):
lines.append("{}".format(line)) for n1_line, n2_line in zip(nail1_lines, nail2_lines):
lines.append("") # Left pad lines to 20 chars, right pad n2 lines
left = "{:<18} ".format(n1_line[:18])
right = "{:<18}".format(n2_line[:18])
lines.append("{}{}{}".format(left, right, Colors.RESET))
# Footer # Footer
lines.append("") lines.append("{}└────────────────────┴────────────────────┘{}".format(
lines.append("{}═════════════════════════════════════════════════════{}".format( Colors.CYAN, Colors.RESET))
Colors.CYAN,
Colors.RESET
))
return "\n".join(lines) return "\n".join(lines)