터치 패널 WAN 속도 자동 단위 변환
This commit is contained in:
1 parent
a3e6ea1c89
commit
a2a45a0118
2 files changed
+56
-16
No files matched your search
@@ -148,7 +148,7 @@ cd /var/www/control
|
|||||||
|
|
||||||
패널은 Control PHP/API/DB snapshot을 호출하지 않는 standalone 방식입니다. Python이 `/sys/class/net`, `/proc/diskstats`, `/sys/class/thermal`, 팬 hwmon 파일을 직접 읽습니다. 상태패널과 팬패널은 50ms 간격으로 커널 카운터를 따라가고, 그래프는 500ms 간격으로 샘플링합니다. 왼쪽 `DISK I/O`, `WAN`, `LAN`, `2.4G`, `5G` 표시등은 장식용 heartbeat가 아니라 직전 샘플 대비 현재 카운터 증가량이 0 초과인지에 반응합니다. 각 타일에는 글자 아래에 세 개의 작은 표시등을 두며 읽기/업로드은 초록, 쓰기/다운로드는 주황, 오류/드롭 증가는 빨강으로 표시합니다. 활동 표시등은 이벤트 감지 후 120ms 동안 유지하고, 오류 표시등은 2초 동안 유지해 너무 짧은 이벤트도 눈에 보이게 합니다. `DISK I/O`의 값은 `/dev/sda` Samsung T7을 `smartctl -d sntasmedia`로 1초마다 읽은 디스크 자체 온도입니다. GUI 사용자 `seo`는 `/etc/sudoers.d/control-touch-panel-smartctl`에서 해당 smartctl 명령만 NOPASSWD로 허용합니다. 네트워크 상태값은 정상, 드롭 누적, 꺼짐으로 표시합니다. 상태패널 최하단의 `PC` 타일은 `192.168.50.73`에 1초마다 ping을 보내 켜짐 여부를 표시하고, 꺼져 있을 때 누르면 `74:56:3C:CD:CD:79`로 WOL magic packet을 보냅니다. PC 타일의 주황은 WOL 후 최대 1분 동안 컴퓨터 응답 대기 표시, 빨강은 magic packet 전송 실패 시 5초 또는 1분 대기 후 응답 실패 시 1분 표시입니다.
|
패널은 Control PHP/API/DB snapshot을 호출하지 않는 standalone 방식입니다. Python이 `/sys/class/net`, `/proc/diskstats`, `/sys/class/thermal`, 팬 hwmon 파일을 직접 읽습니다. 상태패널과 팬패널은 50ms 간격으로 커널 카운터를 따라가고, 그래프는 500ms 간격으로 샘플링합니다. 왼쪽 `DISK I/O`, `WAN`, `LAN`, `2.4G`, `5G` 표시등은 장식용 heartbeat가 아니라 직전 샘플 대비 현재 카운터 증가량이 0 초과인지에 반응합니다. 각 타일에는 글자 아래에 세 개의 작은 표시등을 두며 읽기/업로드은 초록, 쓰기/다운로드는 주황, 오류/드롭 증가는 빨강으로 표시합니다. 활동 표시등은 이벤트 감지 후 120ms 동안 유지하고, 오류 표시등은 2초 동안 유지해 너무 짧은 이벤트도 눈에 보이게 합니다. `DISK I/O`의 값은 `/dev/sda` Samsung T7을 `smartctl -d sntasmedia`로 1초마다 읽은 디스크 자체 온도입니다. GUI 사용자 `seo`는 `/etc/sudoers.d/control-touch-panel-smartctl`에서 해당 smartctl 명령만 NOPASSWD로 허용합니다. 네트워크 상태값은 정상, 드롭 누적, 꺼짐으로 표시합니다. 상태패널 최하단의 `PC` 타일은 `192.168.50.73`에 1초마다 ping을 보내 켜짐 여부를 표시하고, 꺼져 있을 때 누르면 `74:56:3C:CD:CD:79`로 WOL magic packet을 보냅니다. PC 타일의 주황은 WOL 후 최대 1분 동안 컴퓨터 응답 대기 표시, 빨강은 magic packet 전송 실패 시 5초 또는 1분 대기 후 응답 실패 시 1분 표시입니다.
|
||||||
|
|
||||||
오른쪽에는 팬 RPM, CPU 온도, PWM, WAN 실시간 속도와 온도/RPM, WAN 트래픽 그래프를 표시합니다. 위 그래프는 좌측 Y축에 온도, 우측 Y축에 RPM 단위를 표시하고, 아래 WAN 그래프는 기존 형태를 유지합니다. 팬 auto/manual/off 버튼은 없고 PWM 슬라이더는 현재값 위치만 보여 주며 터치 제어는 받지 않습니다.
|
오른쪽에는 팬 RPM, CPU 온도, PWM, WAN 실시간 속도와 온도/RPM, WAN 트래픽 그래프를 표시합니다. WAN 속도와 그래프 Y축은 byte/sec 기준으로 `B/s`, `KB/s`, `MB/s`, `GB/s` 중 자동 변환합니다. 위 그래프는 좌측 Y축에 온도, 우측 Y축에 RPM 단위를 표시하고, 아래 WAN 그래프는 기존 형태를 유지합니다. 팬 auto/manual/off 버튼은 없고 PWM 슬라이더는 현재값 위치만 보여 주며 터치 제어는 받지 않습니다.
|
||||||
|
|
||||||
GUI 자동 실행은 데스크톱 autostart가 1차 기준입니다.
|
GUI 자동 실행은 데스크톱 autostart가 1차 기준입니다.
|
||||||
|
|
||||||
|
|||||||
+55
-15
@@ -31,6 +31,32 @@ PC_IP = "192.168.50.73"
|
|||||||
PC_MAC = "74:56:3C:CD:CD:79"
|
PC_MAC = "74:56:3C:CD:CD:79"
|
||||||
|
|
||||||
|
|
||||||
|
def byte_rate_unit(values):
|
||||||
|
max_value = max([abs(float(value)) for value in values] + [0.0])
|
||||||
|
units = [
|
||||||
|
(1024 ** 3, "GB/s"),
|
||||||
|
(1024 ** 2, "MB/s"),
|
||||||
|
(1024, "KB/s"),
|
||||||
|
]
|
||||||
|
for factor, label in units:
|
||||||
|
if max_value >= factor:
|
||||||
|
return factor, label
|
||||||
|
return 1, "B/s"
|
||||||
|
|
||||||
|
|
||||||
|
def format_byte_rate(value, factor=None, unit=None):
|
||||||
|
if factor is None or unit is None:
|
||||||
|
factor, unit = byte_rate_unit([value])
|
||||||
|
scaled = float(value) / factor
|
||||||
|
if abs(scaled) >= 100:
|
||||||
|
text = f"{scaled:.0f}"
|
||||||
|
elif abs(scaled) >= 10:
|
||||||
|
text = f"{scaled:.1f}"
|
||||||
|
else:
|
||||||
|
text = f"{scaled:.2f}"
|
||||||
|
return f"{text} {unit}"
|
||||||
|
|
||||||
|
|
||||||
def read_text(path, default=""):
|
def read_text(path, default=""):
|
||||||
try:
|
try:
|
||||||
return Path(path).read_text(encoding="utf-8").strip()
|
return Path(path).read_text(encoding="utf-8").strip()
|
||||||
@@ -322,13 +348,14 @@ class MetricCard(QFrame):
|
|||||||
|
|
||||||
|
|
||||||
class Sparkline(QFrame):
|
class Sparkline(QFrame):
|
||||||
def __init__(self, title, color="#38bdf8", second_color=None, y_unit="", right_unit=""):
|
def __init__(self, title, color="#38bdf8", second_color=None, y_unit="", right_unit="", auto_byte_unit=False):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setObjectName("chartCard")
|
self.setObjectName("chartCard")
|
||||||
self.color = QColor(color)
|
self.color = QColor(color)
|
||||||
self.second_color = QColor(second_color) if second_color else None
|
self.second_color = QColor(second_color) if second_color else None
|
||||||
self.y_unit = y_unit
|
self.y_unit = y_unit
|
||||||
self.right_unit = right_unit
|
self.right_unit = right_unit
|
||||||
|
self.auto_byte_unit = auto_byte_unit
|
||||||
self.values = []
|
self.values = []
|
||||||
self.second_values = []
|
self.second_values = []
|
||||||
self.title = QLabel(title)
|
self.title = QLabel(title)
|
||||||
@@ -366,17 +393,25 @@ class Sparkline(QFrame):
|
|||||||
painter.drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom())
|
painter.drawLine(rect.left(), rect.bottom(), rect.right(), rect.bottom())
|
||||||
if len(self.values) < 2:
|
if len(self.values) < 2:
|
||||||
return
|
return
|
||||||
low = min(self.values)
|
values = list(self.values)
|
||||||
high = max(self.values)
|
second_values = list(self.second_values)
|
||||||
|
unit_factor = 1
|
||||||
|
unit_label = self.y_unit
|
||||||
|
if self.auto_byte_unit:
|
||||||
|
unit_factor, unit_label = byte_rate_unit(values + second_values)
|
||||||
|
values = [value / unit_factor for value in values]
|
||||||
|
second_values = [value / unit_factor for value in second_values]
|
||||||
|
low = min(values)
|
||||||
|
high = max(values)
|
||||||
if high <= low:
|
if high <= low:
|
||||||
high = low + 1
|
high = low + 1
|
||||||
painter.setPen(QColor("#93a4bd"))
|
painter.setPen(QColor("#93a4bd"))
|
||||||
painter.drawText(1, rect.top() + 10, self.axis_text(high))
|
painter.drawText(1, rect.top() + 10, self.axis_text(high, unit_label))
|
||||||
painter.drawText(1, rect.bottom(), self.axis_text(low))
|
painter.drawText(1, rect.bottom(), self.axis_text(low, unit_label))
|
||||||
self.draw_series(painter, rect, self.values, low, high, self.color)
|
self.draw_series(painter, rect, values, low, high, self.color)
|
||||||
if self.second_color and self.second_values:
|
if self.second_color and second_values:
|
||||||
second_low = min(self.second_values)
|
second_low = min(second_values)
|
||||||
second_high = max(self.second_values)
|
second_high = max(second_values)
|
||||||
if second_high <= second_low:
|
if second_high <= second_low:
|
||||||
second_high = second_low + 1
|
second_high = second_low + 1
|
||||||
if self.right_unit:
|
if self.right_unit:
|
||||||
@@ -384,7 +419,9 @@ class Sparkline(QFrame):
|
|||||||
right_x = self.canvas.rect().right() - 31
|
right_x = self.canvas.rect().right() - 31
|
||||||
painter.drawText(right_x, rect.top() + 10, self.axis_text(second_high, self.right_unit))
|
painter.drawText(right_x, rect.top() + 10, self.axis_text(second_high, self.right_unit))
|
||||||
painter.drawText(right_x, rect.bottom(), self.axis_text(second_low, self.right_unit))
|
painter.drawText(right_x, rect.bottom(), self.axis_text(second_low, self.right_unit))
|
||||||
self.draw_series(painter, rect, self.second_values, second_low, second_high, self.second_color)
|
self.draw_series(painter, rect, second_values, second_low, second_high, self.second_color)
|
||||||
|
else:
|
||||||
|
self.draw_series(painter, rect, second_values, low, high, self.second_color)
|
||||||
|
|
||||||
def axis_text(self, value, unit=None):
|
def axis_text(self, value, unit=None):
|
||||||
if abs(value) >= 100:
|
if abs(value) >= 100:
|
||||||
@@ -524,7 +561,7 @@ class TouchPanel(QMainWindow):
|
|||||||
chart_grid.setVerticalSpacing(8)
|
chart_grid.setVerticalSpacing(8)
|
||||||
for index, (key, chart) in enumerate([
|
for index, (key, chart) in enumerate([
|
||||||
("temp", Sparkline("온도 / RPM", "#f97316", "#38bdf8", "C", "rpm")),
|
("temp", Sparkline("온도 / RPM", "#f97316", "#38bdf8", "C", "rpm")),
|
||||||
("wan", Sparkline("WAN 트래픽", "#38bdf8", "#22c55e", "M")),
|
("wan", Sparkline("WAN 트래픽", "#38bdf8", "#22c55e", auto_byte_unit=True)),
|
||||||
]):
|
]):
|
||||||
self.charts[key] = chart
|
self.charts[key] = chart
|
||||||
chart_grid.addWidget(chart, index, 0)
|
chart_grid.addWidget(chart, index, 0)
|
||||||
@@ -572,8 +609,8 @@ class TouchPanel(QMainWindow):
|
|||||||
}
|
}
|
||||||
if previous:
|
if previous:
|
||||||
rates[key] = {
|
rates[key] = {
|
||||||
"rx": max(0.0, (current[0] - previous[0]) * 8 / dt / 1_000_000),
|
"rx": max(0.0, (current[0] - previous[0]) / dt),
|
||||||
"tx": max(0.0, (current[1] - previous[1]) * 8 / dt / 1_000_000),
|
"tx": max(0.0, (current[1] - previous[1]) / dt),
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
rates[key] = {"rx": 0.0, "tx": 0.0}
|
rates[key] = {"rx": 0.0, "tx": 0.0}
|
||||||
@@ -593,7 +630,10 @@ class TouchPanel(QMainWindow):
|
|||||||
self.metrics["rpm"].set_value(f"{rpm:,}")
|
self.metrics["rpm"].set_value(f"{rpm:,}")
|
||||||
self.metrics["temp"].set_value(f"{temp:.1f} C")
|
self.metrics["temp"].set_value(f"{temp:.1f} C")
|
||||||
self.metrics["pwm"].set_value(f"{pwm} / {percent:.1f}%")
|
self.metrics["pwm"].set_value(f"{pwm} / {percent:.1f}%")
|
||||||
self.metrics["wan"].set_value(f"{rates['wan']['rx']:.2f}/{rates['wan']['tx']:.2f} Mbps")
|
wan_factor, wan_unit = byte_rate_unit([rates["wan"]["rx"], rates["wan"]["tx"]])
|
||||||
|
rx_text = format_byte_rate(rates["wan"]["rx"], wan_factor, wan_unit)
|
||||||
|
tx_text = format_byte_rate(rates["wan"]["tx"], wan_factor, wan_unit)
|
||||||
|
self.metrics["wan"].set_value(f"{rx_text}/{tx_text}")
|
||||||
|
|
||||||
self.slider.blockSignals(True)
|
self.slider.blockSignals(True)
|
||||||
self.slider.setValue(pwm)
|
self.slider.setValue(pwm)
|
||||||
@@ -641,7 +681,7 @@ class TouchPanel(QMainWindow):
|
|||||||
self.net_history["wan"]["tx"].append(wan["tx"])
|
self.net_history["wan"]["tx"].append(wan["tx"])
|
||||||
self.charts["wan"].set_values(
|
self.charts["wan"].set_values(
|
||||||
self.net_history["wan"]["rx"],
|
self.net_history["wan"]["rx"],
|
||||||
f"{wan['rx']:.2f}/{wan['tx']:.2f} Mbps",
|
f"{format_byte_rate(wan['rx'])}/{format_byte_rate(wan['tx'])}",
|
||||||
self.net_history["wan"]["tx"],
|
self.net_history["wan"]["tx"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user