diff --git a/piNail2/tty_status_display.py b/piNail2/tty_status_display.py index 7931fbb..db3c13b 100644 --- a/piNail2/tty_status_display.py +++ b/piNail2/tty_status_display.py @@ -148,79 +148,69 @@ class StatusDisplay: color, label = phases.get(phase, (Colors.WHITE, " ? ")) return "{}{}{}".format(color, label, Colors.RESET) - def draw_nail_panel(self, nail_id, status): - """Draw status panel for a single nail.""" + def draw_nail_panel_compact(self, nail_id, status): + """Draw compact status panel for a single nail (for side-by-side layout).""" if not status: return [] lines = [] # 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) has_error = status.get("error", False) icon = self.format_status_icon(enabled, has_error) - lines.append("{}{} {} {}{}".format( + lines.append("{}{}{}{}".format( Colors.BOLD, nail_name, icon, - "ONLINE" if enabled else "OFFLINE", - Colors.RESET - )) + "ON" if enabled else "OFF" + ) + Colors.RESET) # Temperature display current = float(status.get("current_temp", 0)) setpoint = float(status.get("setpoint", 0)) - lines.append(" Temp: {} / {}{}C".format( - self.format_temp(current), - self.format_temp(setpoint), - Colors.RESET + lines.append("T:{} S:{}".format( + "{:5.0f}F".format(current) if current else "ERR", + "{:5.0f}F".format(setpoint) )) # Error display error = float(status.get("error", 0)) 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_pct = min(100.0, max(0.0, output * 100)) - bar_filled = int(output_pct / 5) + bar_filled = int(output_pct / 10) bar = "{}|{}{}|{}".format( Colors.BRIGHT_GREEN, "=" * bar_filled, - " " * (20 - bar_filled), + " " * (10 - bar_filled), Colors.RESET ) - lines.append(" Output: {} {:5.1f}%".format(bar, output_pct)) + lines.append("Out: {} {:3.0f}%".format(bar, output_pct)) # Flight mode and phase mode = status.get("flight_mode", "grounded") phase = status.get("phase", "idle") - lines.append(" Mode: {} Phase: {}".format( - self.format_mode_badge(mode), - self.format_phase_badge(phase) + mode_short = mode[:4].upper() + phase_short = phase[:4].upper() + 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.get("safety", {}) temp_ok = safety.get("temp_ok", True) tc_ok = safety.get("tc_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) - lines.append(" Safety: {}".format(safety_status)) + 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_status) return lines @@ -231,25 +221,33 @@ class StatusDisplay: # ASCII Art Title with timestamp side-by-side timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") lines.append("") - lines.append("{}{}{:<20} {}{}{}".format( + lines.append("{}{}{:<22} {}{}{}".format( Colors.BRIGHT_CYAN + Colors.BOLD, - " ___ _ _ _ _", + " ___ __ _ _ _ _", "", Colors.DIM, timestamp, Colors.RESET )) - lines.append("{}{}{:<20} {}{}{}".format( + lines.append("{}{}{:<22} {}{}{}".format( Colors.BRIGHT_CYAN + Colors.BOLD, - "| \\| | __ _ _ _| | ||_|", + " / _ \\ / / | \\| | | | |", "", Colors.DIM, - "piNail2 Status", + "piNail2 e-Nail", Colors.RESET )) - lines.append("{}{}{:<20}{}".format( + lines.append("{}{}{:<22} {}{}{}".format( Colors.BRIGHT_CYAN + Colors.BOLD, - "|__/|_|__|||_||_||_|\\__/", + "| |_| |/ /__ \\ / |_|_|", + "", + Colors.DIM, + "Temperature Controller", + Colors.RESET + )) + lines.append("{}{}{:<22}{}".format( + Colors.BRIGHT_CYAN + Colors.BOLD, + " \\___/______| \\/ ", "", Colors.RESET )) @@ -261,29 +259,30 @@ class StatusDisplay: nail1_data = self.data.get("nail1", {}) nail2_data = self.data.get("nail2", {}) - # Nail 1 panel - lines.append("{}┌─ NAIL 1 {}".format(Colors.CYAN, Colors.RESET)) - lines.append("│") - for line in self.draw_nail_panel("nail1", nail1_data): - lines.append("│ {}".format(line)) - lines.append("│") + # Get compact panel lines for both nails + nail1_lines = self.draw_nail_panel_compact("nail1", nail1_data) + nail2_lines = self.draw_nail_panel_compact("nail2", nail2_data) - # Separator - lines.append("") + # Pad lines to same length + 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 - lines.append("{}┌─ NAIL 2 {}".format(Colors.CYAN, Colors.RESET)) - lines.append("│") - for line in self.draw_nail_panel("nail2", nail2_data): - lines.append("│ {}".format(line)) - lines.append("│") + # Draw side-by-side panels + lines.append("{}┌─ NAIL 1 ────────────┬─ NAIL 2 ────────────┐{}".format( + Colors.CYAN, Colors.RESET)) + + for n1_line, n2_line in zip(nail1_lines, nail2_lines): + # 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 - lines.append("") - lines.append("{}═════════════════════════════════════════════════════{}".format( - Colors.CYAN, - Colors.RESET - )) + lines.append("{}└────────────────────┴────────────────────┘{}".format( + Colors.CYAN, Colors.RESET)) return "\n".join(lines)