터치 패널 사이드바 축소와 팬 표시 오류 수정

This commit is contained in:
seo committed 2026-07-30 04:43:39 +09:00
1 parent 5bfac4429a
commit e99fe1021a
2 files changed
+103 -43

No files matched your search

+64 -2
View File
@@ -4,6 +4,68 @@ declare(strict_types=1);
define('CONTROL_API_LIBRARY', true); define('CONTROL_API_LIBRARY', true);
require_once __DIR__ . '/../public/api.php'; require_once __DIR__ . '/../public/api.php';
function touch_panel_snapshot(array $snapshot): array
{
$network = is_array($snapshot['network'] ?? null) ? $snapshot['network'] : [];
$wifi = is_array($snapshot['wifi'] ?? null) ? $snapshot['wifi'] : [];
$system = is_array($snapshot['system'] ?? null) ? $snapshot['system'] : [];
$fan = is_array($snapshot['fan'] ?? null) ? $snapshot['fan'] : [];
$touch = is_array($snapshot['touch_display'] ?? null) ? $snapshot['touch_display'] : [];
$interfaces = [];
foreach (($network['interfaces'] ?? []) as $row) {
if (!is_array($row) || !isset($row['name'])) {
continue;
}
$interfaces[] = [
'name' => $row['name'],
'state' => $row['state'] ?? null,
'carrier' => $row['carrier'] ?? null,
'rx_errors' => $row['rx_errors'] ?? 0,
'tx_errors' => $row['tx_errors'] ?? 0,
'rx_dropped' => $row['rx_dropped'] ?? 0,
'tx_dropped' => $row['tx_dropped'] ?? 0,
];
}
$clients = [];
foreach (($wifi['clients'] ?? []) as $client) {
if (!is_array($client)) {
continue;
}
$clients[] = [
'band' => $client['band'] ?? null,
];
}
return [
'ok' => (bool)($snapshot['ok'] ?? true),
'generated_at' => $snapshot['generated_at'] ?? null,
'fan' => [
'mode' => $fan['mode'] ?? 'auto',
'pwm' => $fan['pwm'] ?? 0,
'percent' => $fan['percent'] ?? 0,
'rpm' => $fan['rpm'] ?? 0,
'control_temp_c' => $fan['control_temp_c'] ?? null,
'policy_ok' => $fan['policy_ok'] ?? true,
],
'system' => [
'hostname' => $system['hostname'] ?? null,
'temp_c' => $system['temp_c'] ?? null,
],
'network' => [
'default_interface' => $network['default_interface'] ?? null,
'interfaces' => $interfaces,
],
'wifi' => [
'clients' => $clients,
],
'touch_display' => [
'connected' => $touch['connected'] ?? false,
],
];
}
function touch_panel_json(array $payload, int $code = 0): never function touch_panel_json(array $payload, int $code = 0): never
{ {
fwrite(STDOUT, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL); fwrite(STDOUT, json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL);
@@ -16,7 +78,7 @@ try {
if ($command === 'status') { if ($command === 'status') {
touch_panel_json([ touch_panel_json([
'ok' => true, 'ok' => true,
'data' => collect_snapshot(false), 'data' => touch_panel_snapshot(collect_snapshot(false)),
]); ]);
} }
@@ -41,7 +103,7 @@ try {
touch_panel_json([ touch_panel_json([
'ok' => true, 'ok' => true,
'data' => collect_snapshot(false), 'data' => touch_panel_snapshot(collect_snapshot(false)),
]); ]);
} }
+39 -41
View File
@@ -9,7 +9,6 @@ from PyQt5.QtGui import QColor, QFont, QPainter
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QApplication, QApplication,
QFrame, QFrame,
QGridLayout,
QHBoxLayout, QHBoxLayout,
QLabel, QLabel,
QMainWindow, QMainWindow,
@@ -49,7 +48,7 @@ class StatusDot(QWidget):
self.active = False self.active = False
self.warning = False self.warning = False
self.pulse = 0 self.pulse = 0
self.setFixedSize(34, 34) self.setFixedSize(22, 22)
def set_state(self, active, warning=False): def set_state(self, active, warning=False):
self.active = active self.active = active
@@ -73,9 +72,9 @@ class StatusDot(QWidget):
base = QColor("#86efac") if self.pulse % 2 == 0 else QColor("#16a34a") base = QColor("#86efac") if self.pulse % 2 == 0 else QColor("#16a34a")
painter.setBrush(base) painter.setBrush(base)
painter.setPen(Qt.NoPen) painter.setPen(Qt.NoPen)
painter.drawEllipse(4, 4, 26, 26) painter.drawEllipse(3, 3, 16, 16)
painter.setBrush(QColor(255, 255, 255, 55)) painter.setBrush(QColor(255, 255, 255, 55))
painter.drawEllipse(10, 8, 8, 8) painter.drawEllipse(8, 6, 5, 5)
class StatusTile(QFrame): class StatusTile(QFrame):
@@ -88,16 +87,20 @@ class StatusTile(QFrame):
self.detail.setObjectName("statusDetail") self.detail.setObjectName("statusDetail")
self.detail.setWordWrap(True) self.detail.setWordWrap(True)
self.detail.setAlignment(Qt.AlignCenter) self.detail.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.setFixedHeight(58)
layout = QVBoxLayout(self) layout = QHBoxLayout(self)
layout.setContentsMargins(8, 7, 8, 8) layout.setContentsMargins(7, 6, 7, 6)
layout.setSpacing(4) layout.setSpacing(6)
layout.addWidget(self.title, 0, Qt.AlignLeft) layout.addWidget(self.dot, 0, Qt.AlignLeft | Qt.AlignVCenter)
layout.addStretch(1)
layout.addWidget(self.dot, 0, Qt.AlignCenter) text_layout = QVBoxLayout()
layout.addStretch(1) text_layout.setContentsMargins(0, 0, 0, 0)
layout.addWidget(self.detail) text_layout.setSpacing(1)
text_layout.addWidget(self.title)
text_layout.addWidget(self.detail)
layout.addLayout(text_layout, 1)
self.setObjectName("statusTile") self.setObjectName("statusTile")
def update_state(self, active, detail, warning=False): def update_state(self, active, detail, warning=False):
@@ -140,35 +143,31 @@ class TouchPanel(QMainWindow):
def _build_ui(self): def _build_ui(self):
root = QWidget() root = QWidget()
grid = QGridLayout(root) layout = QHBoxLayout(root)
grid.setContentsMargins(16, 14, 16, 14) layout.setContentsMargins(4, 8, 10, 8)
grid.setHorizontalSpacing(14) layout.setSpacing(8)
grid.setVerticalSpacing(10)
left = QFrame() left = QFrame()
left.setObjectName("panel") left.setObjectName("statusPanel")
left.setFixedWidth(132)
left_layout = QVBoxLayout(left) left_layout = QVBoxLayout(left)
left_layout.setContentsMargins(14, 14, 14, 14) left_layout.setContentsMargins(6, 8, 6, 8)
left_layout.setSpacing(8) left_layout.setSpacing(6)
title = QLabel("상태") title = QLabel("상태")
title.setObjectName("sectionTitle") title.setObjectName("sectionTitle")
left_layout.addWidget(title) left_layout.addWidget(title)
status_grid = QGridLayout() for key, label in [
status_grid.setHorizontalSpacing(8)
status_grid.setVerticalSpacing(8)
for index, (key, label) in enumerate([
("io", "IO"), ("io", "IO"),
("wan", "WAN"), ("wan", "WAN"),
("lan", "LAN"), ("lan", "LAN"),
("wifi24", "2.4G"), ("wifi24", "2.4G"),
("wifi5", "5G"), ("wifi5", "5G"),
]): ]:
row = StatusTile(label) row = StatusTile(label)
self.rows[key] = row self.rows[key] = row
status_grid.addWidget(row, index // 2, index % 2) left_layout.addWidget(row)
left_layout.addLayout(status_grid, 1)
left_layout.addStretch(1) left_layout.addStretch(1)
right = QFrame() right = QFrame()
@@ -228,10 +227,8 @@ class TouchPanel(QMainWindow):
right_layout.addWidget(self.notice) right_layout.addWidget(self.notice)
right_layout.addStretch(1) right_layout.addStretch(1)
grid.addWidget(left, 0, 0) layout.addWidget(left)
grid.addWidget(right, 0, 1) layout.addWidget(right, 1)
grid.setColumnStretch(0, 3)
grid.setColumnStretch(1, 4)
self.setCentralWidget(root) self.setCentralWidget(root)
def tick_pulses(self): def tick_pulses(self):
@@ -295,12 +292,12 @@ class TouchPanel(QMainWindow):
w5 = interfaces.get("wlan1") or {} w5 = interfaces.get("wlan1") or {}
io_active = bool(touch.get("connected")) and bool(fan.get("policy_ok", True)) io_active = bool(touch.get("connected")) and bool(fan.get("policy_ok", True))
io_detail = f"{system.get('hostname', '-')} / {temp:.1f} C" io_detail = f"{temp:.1f} C"
self.rows["io"].update_state(io_active, io_detail, temp >= 75) self.rows["io"].update_state(io_active, io_detail, temp >= 75)
self.rows["wan"].update_state(self.iface_up(wan), self.iface_detail(wan), self.iface_warn(wan)) self.rows["wan"].update_state(self.iface_up(wan), self.iface_detail(wan), self.iface_warn(wan))
self.rows["lan"].update_state(self.iface_up(lan), f"{len(lan_clients)}대 / {self.iface_detail(lan)}", self.iface_warn(lan)) self.rows["lan"].update_state(self.iface_up(lan), f"{len(lan_clients)}대 {self.iface_detail(lan)}", self.iface_warn(lan))
self.rows["wifi24"].update_state(self.iface_up(w24), f"{len(wifi24)}대 / {self.iface_detail(w24)}", self.iface_warn(w24)) self.rows["wifi24"].update_state(self.iface_up(w24), f"{len(wifi24)}대 {self.iface_detail(w24)}", self.iface_warn(w24))
self.rows["wifi5"].update_state(self.iface_up(w5), f"{len(wifi5)}대 / {self.iface_detail(w5)}", self.iface_warn(w5)) self.rows["wifi5"].update_state(self.iface_up(w5), f"{len(wifi5)}대 {self.iface_detail(w5)}", self.iface_warn(w5))
self.current_mode = mode self.current_mode = mode
self.current_pwm = pwm self.current_pwm = pwm
@@ -328,7 +325,7 @@ class TouchPanel(QMainWindow):
return "없음" return "없음"
rx_drop = int(row.get("rx_dropped") or 0) rx_drop = int(row.get("rx_dropped") or 0)
tx_drop = int(row.get("tx_dropped") or 0) tx_drop = int(row.get("tx_dropped") or 0)
return f"{row.get('name', '-')} {row.get('state', '-')} / drop {rx_drop + tx_drop}" return f"{row.get('state', '-')} D{rx_drop + tx_drop}"
def main(): def main():
@@ -336,12 +333,13 @@ def main():
app.setStyleSheet( app.setStyleSheet(
""" """
QWidget { background: #0b1220; color: #e5edf7; font-family: "Noto Sans CJK KR", "Noto Sans KR", sans-serif; } QWidget { background: #0b1220; color: #e5edf7; font-family: "Noto Sans CJK KR", "Noto Sans KR", sans-serif; }
#panel { background: #111a2e; border: 1px solid #27364f; border-radius: 18px; } #panel { background: #111a2e; border: 1px solid #27364f; border-radius: 16px; }
#sectionTitle { font-size: 24px; font-weight: 700; } #statusPanel { background: #101827; border-right: 1px solid #263650; }
#sectionTitle { font-size: 20px; font-weight: 700; }
#statusTile { background: #d7d9dc; border: 2px groove #f4f4f4; border-radius: 2px; } #statusTile { background: #d7d9dc; border: 2px groove #f4f4f4; border-radius: 2px; }
#statusTitle { font-size: 15px; font-weight: 700; color: #111827; background: transparent; } #statusTitle { font-size: 13px; font-weight: 700; color: #111827; background: transparent; }
#statusDetail { font-size: 11px; color: #475569; background: transparent; } #statusDetail { font-size: 10px; color: #475569; background: transparent; }
#bigValue { font-size: 56px; font-weight: 800; color: #f8fafc; } #bigValue { font-size: 62px; font-weight: 800; color: #f8fafc; }
#subValue { font-size: 22px; color: #b8c5d8; } #subValue { font-size: 22px; color: #b8c5d8; }
#clock { font-size: 18px; color: #8fb3ff; font-weight: 700; } #clock { font-size: 18px; color: #8fb3ff; font-weight: 700; }
#notice { padding: 10px; background: #17233a; border: 1px solid #2b3c59; border-radius: 12px; color: #a8b8d0; } #notice { padding: 10px; background: #17233a; border: 1px solid #2b3c59; border-radius: 12px; color: #a8b8d0; }