팬 정책 데몬 전환과 관측 로그 정리

This commit is contained in:
seo committed 2026-08-02 00:49:38 +09:00
1 parent cfd5bb9b94
commit 309c10f1a1
4 files changed
+65 -11

No files matched your search

+6 -3
View File
@@ -6,7 +6,7 @@
Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를 웹에서 관리하기 위한 내부 운영 도구입니다. 로그인 후 대시보드에서 온도, 팬 RPM, PWM, WiFi client, 저전압/스로틀링, notice를 확인하고 필요한 제어 명령을 실행합니다. 첫 화면은 운영 판단에 필요한 핵심 정보 위주로 구성하고, 상세 차트와 진단성 데이터는 접힘 영역에 둡니다. Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를 웹에서 관리하기 위한 내부 운영 도구입니다. 로그인 후 대시보드에서 온도, 팬 RPM, PWM, WiFi client, 저전압/스로틀링, notice를 확인하고 필요한 제어 명령을 실행합니다. 첫 화면은 운영 판단에 필요한 핵심 정보 위주로 구성하고, 상세 차트와 진단성 데이터는 접힘 영역에 둡니다.
상태 갱신은 WebSocket을 우선 사용하고, 연결 실패 시 HTTP fallback으로 전환합니다. 팬 정책 적용과 센서 수집은 백그라운드 작업과 API 호출을 통해 DB에 기록됩니다. 상태 갱신은 WebSocket을 우선 사용하고, 연결 실패 시 HTTP fallback으로 전환합니다. 팬 정책 적용과 센서 수집은 백그라운드 daemon과 API 호출을 통해 DB에 기록됩니다.
## 주요 기능 ## 주요 기능
@@ -64,7 +64,7 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
- `public/assets/app.js`: 대시보드 렌더링, WebSocket, 차트, 조작 이벤트 - `public/assets/app.js`: 대시보드 렌더링, WebSocket, 차트, 조작 이벤트
- `public/assets/wakelock.js`: Screen Wake Lock API 제어 - `public/assets/wakelock.js`: Screen Wake Lock API 제어
- `config/config.php`: DB, 인증, CSRF, shell 실행 공통 함수 - `config/config.php`: DB, 인증, CSRF, shell 실행 공통 함수
- `apply_policy.php`: CLI/cron 팬 정책 적용 - `apply_policy.php`: CLI 1회 실행과 daemon 모드를 지원하는 팬 정책 적용 루프
- `bin/control_ws.php`: WebSocket 서버. 핵심 PHP 파일 변경 감지 시 종료되고 systemd가 재시작해 새 코드를 로드 - `bin/control_ws.php`: WebSocket 서버. 핵심 PHP 파일 변경 감지 시 종료되고 systemd가 재시작해 새 코드를 로드
- `bin/wifi_observe.php`: 5G WiFi 연결 시간 보정을 위한 시스템단 관측 CLI - `bin/wifi_observe.php`: 5G WiFi 연결 시간 보정을 위한 시스템단 관측 CLI
- `touch_panel/panel.py`: 라즈베리파이 정품 터치 디스플레이용 standalone Qt 대시보드 - `touch_panel/panel.py`: 라즈베리파이 정품 터치 디스플레이용 standalone Qt 대시보드
@@ -72,6 +72,7 @@ Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를
- `touch_panel/control_touch_bridge.php`: 이전 Qt 패널용 로컬 JSON 브리지. 현재 standalone 패널은 사용하지 않습니다. - `touch_panel/control_touch_bridge.php`: 이전 Qt 패널용 로컬 JSON 브리지. 현재 standalone 패널은 사용하지 않습니다.
- `touch_panel/control-touch-panel.desktop`: GUI 로그인 후 Qt 패널을 띄우는 데스크톱 autostart 파일 - `touch_panel/control-touch-panel.desktop`: GUI 로그인 후 Qt 패널을 띄우는 데스크톱 autostart 파일
- `systemd/control-rpi-disk-temp.service`: 터치 패널용 SSD 온도 캐시 서비스 예시 - `systemd/control-rpi-disk-temp.service`: 터치 패널용 SSD 온도 캐시 서비스 예시
- `systemd/fanpanel-apply.service`: 팬 정책을 1초 간격으로 계속 실행하는 daemon unit 예시
- `systemd/control-wifi-observe.*`: WiFi 관측 CLI를 주기적으로 실행하는 systemd unit 예시 - `systemd/control-wifi-observe.*`: WiFi 관측 CLI를 주기적으로 실행하는 systemd unit 예시
- `systemd/control-touch-panel.service`: GUI 세션에서 Qt 관리자 패널을 띄우는 systemd unit 예시 - `systemd/control-touch-panel.service`: GUI 세션에서 Qt 관리자 패널을 띄우는 systemd unit 예시
@@ -168,6 +169,7 @@ systemd 방식이 필요하면 `systemd/control-touch-panel.service`를 `/etc/sy
- 탭 복귀: 즉시 1회 상태 갱신 - 탭 복귀: 즉시 1회 상태 갱신
- UPS/배터리 잔여 시간: 현재 운영 구성에서는 표시하지 않음 - UPS/배터리 잔여 시간: 현재 운영 구성에서는 표시하지 않음
- dmesg: 진단 패널에서 열었을 때만 1초마다 갱신, 닫으면 중지 - dmesg: 진단 패널에서 열었을 때만 1초마다 갱신, 닫으면 중지
- 팬 정책 daemon: `fanpanel-apply.service`가 `apply_policy.php --daemon`을 실행해 1초마다 팬 정책과 센서 로그를 처리
- WiFi 관측 보정 timer: 10초마다 `bin/wifi_observe.php` 실행 - WiFi 관측 보정 timer: 10초마다 `bin/wifi_observe.php` 실행
- 외출/귀가 팬 정책: 팬 정책 적용 루프에서 기본 30초마다 서울 HA의 휴대폰 device tracker를 확인 - 외출/귀가 팬 정책: 팬 정책 적용 루프에서 기본 30초마다 서울 HA의 휴대폰 device tracker를 확인
- WebSocket 소스 변경 감지: 15초마다 확인 후 재시작 - WebSocket 소스 변경 감지: 15초마다 확인 후 재시작
@@ -205,7 +207,7 @@ control-wifi-observe.service
/usr/bin/php /var/www/control/bin/wifi_observe.php /usr/bin/php /var/www/control/bin/wifi_observe.php
``` ```
이 작업은 팬 정책, 차트 로그 저장, System Notice 기록과 분리되어 있으며 WiFi/LAN client 목록 확인과 `wifi_observed_sessions` 갱신만 수행합니다. 이 작업은 팬 정책, 차트 로그 저장, System Notice 기록과 분리되어 있으며 WiFi/LAN client 목록 확인과 `wifi_observed_sessions` 갱신만 수행합니다. 정상 실행 JSON은 운영 기능에 쓰지 않으므로 systemd unit에서는 stdout을 버리고 실패 stderr만 journal에 남깁니다.
호스트명은 dnsmasq lease 값을 우선 사용합니다. lease hostname이 `N/A`이면 MAC 주소 끝 6자리 기반 임시명 `기기-XXXXXX`을 표시하고, 표의 호스트명을 클릭해 저장한 수동 이름은 `wifi_client_aliases`에 보관해 이후에도 우선 표시합니다. 호스트명은 dnsmasq lease 값을 우선 사용합니다. lease hostname이 `N/A`이면 MAC 주소 끝 6자리 기반 임시명 `기기-XXXXXX`을 표시하고, 표의 호스트명을 클릭해 저장한 수동 이름은 `wifi_client_aliases`에 보관해 이후에도 우선 표시합니다.
@@ -302,6 +304,7 @@ control-wifi-observe.service
- 2.4G 내장 WiFi는 `hostapd-24g.service` 개별 restart 직후 일부 IoT 단말이 WPA/EAPOL 재협상 루프에 들어갈 수 있으므로, 안정화된 상태에서는 개별 restart를 피하고 필요 시 전체 reboot 또는 채널 변경으로 재초기화합니다. - 2.4G 내장 WiFi는 `hostapd-24g.service` 개별 restart 직후 일부 IoT 단말이 WPA/EAPOL 재협상 루프에 들어갈 수 있으므로, 안정화된 상태에서는 개별 restart를 피하고 필요 시 전체 reboot 또는 채널 변경으로 재초기화합니다.
- `hostapd-24g.service`, `hostapd-5g.service`는 종료 후 해당 `wlan` 인터페이스를 `br0`에서 떼도록 두어, 모듈이 빠졌다가 돌아온 뒤 hostapd가 다시 붙이는 흐름으로 정리합니다. - `hostapd-24g.service`, `hostapd-5g.service`는 종료 후 해당 `wlan` 인터페이스를 `br0`에서 떼도록 두어, 모듈이 빠졌다가 돌아온 뒤 hostapd가 다시 붙이는 흐름으로 정리합니다.
- 5G WiFi 연결 시간은 외부 모듈이 값을 제공하지 않을 때 서버가 처음 감지한 시각 기준으로 계산합니다. 현재 5G 목록에서 MAC이 사라지면 관측 세션을 즉시 종료하므로 재연결 시 0부터 다시 누적됩니다. - 5G WiFi 연결 시간은 외부 모듈이 값을 제공하지 않을 때 서버가 처음 감지한 시각 기준으로 계산합니다. 현재 5G 목록에서 MAC이 사라지면 관측 세션을 즉시 종료하므로 재연결 시 0부터 다시 누적됩니다.
- `fanpanel-apply.service`는 `/var/www/control/apply_policy.php --daemon`으로 상주하며, 기존 1초 timer 방식과 같은 팬 정책/센서 로그 처리를 프로세스 재생성 없이 반복합니다.
- `control-wifi-observe.timer`는 10초마다 `/var/www/control/bin/wifi_observe.php`를 실행해 사용자가 대시보드에 접속하지 않아도 5G/LAN 연결 시간 카운터를 시작하고 유지합니다. - `control-wifi-observe.timer`는 10초마다 `/var/www/control/bin/wifi_observe.php`를 실행해 사용자가 대시보드에 접속하지 않아도 5G/LAN 연결 시간 카운터를 시작하고 유지합니다.
- WebSocket은 장기 실행 프로세스이므로 `public/api.php`, `config/config.php`, `bin/control_ws.php` 변경을 감지하면 15초 안에 종료되고 `control-websocket.service`가 새 프로세스로 재시작합니다. - WebSocket은 장기 실행 프로세스이므로 `public/api.php`, `config/config.php`, `bin/control_ws.php` 변경을 감지하면 15초 안에 종료되고 `control-websocket.service`가 새 프로세스로 재시작합니다.
- WebSocket 장기 실행 중 DB 연결이 끊길 수 있으므로 reconnect 로그를 확인합니다. - WebSocket 장기 실행 중 DB 연결이 끊길 수 있으므로 reconnect 로그를 확인합니다.
+38 -6
View File
@@ -3,6 +3,8 @@ declare(strict_types=1);
require_once __DIR__ . '/config/config.php'; require_once __DIR__ . '/config/config.php';
const FAN_POLICY_INTERVAL_SECONDS = 1.0;
function local_fan_paths(): array function local_fan_paths(): array
{ {
$candidates = glob('/sys/devices/platform/cooling_fan/hwmon/hwmon*/pwm1') ?: []; $candidates = glob('/sys/devices/platform/cooling_fan/hwmon/hwmon*/pwm1') ?: [];
@@ -169,7 +171,8 @@ function dmesg_log_throttled(string $key, string $message, int $seconds = 30): v
dmesg_log($message); dmesg_log($message);
} }
try { function run_fan_policy_once(): bool
{
sync_presence_fan_policy(); sync_presence_fan_policy();
$state = get_control_state(); $state = get_control_state();
@@ -200,15 +203,12 @@ try {
usleep(80000); usleep(80000);
$actualPwm = read_int_file($paths['pwm']); $actualPwm = read_int_file($paths['pwm']);
$rpm = read_int_file($paths['rpm']); $rpm = read_int_file($paths['rpm']);
$load = sys_getloadavg() ?: [0, 0, 0]; $load = sys_getloadavg() ?: [0, 0, 0];
$mem = local_mem(); $mem = local_mem();
$disk = local_disk('/'); $disk = local_disk('/');
$cpuPower = cpu_power_status(); $cpuPower = cpu_power_status();
$battery = battery_status(); $battery = battery_status();
$ok = $enableOk && $pwmOk; $ok = $enableOk && $pwmOk;
add_sensor_log([ add_sensor_log([
@@ -254,8 +254,11 @@ try {
); );
} }
exit($ok ? 0 : 1); return $ok;
} catch (Throwable $e) { }
function report_fan_policy_exception(Throwable $e): void
{
try { try {
add_fan_action( add_fan_action(
'apply_policy_exception', 'apply_policy_exception',
@@ -268,5 +271,34 @@ try {
} }
fwrite(STDERR, $e->getMessage() . PHP_EOL); fwrite(STDERR, $e->getMessage() . PHP_EOL);
}
function run_fan_policy_daemon(): int
{
while (true) {
$started = microtime(true);
try {
run_fan_policy_once();
} catch (Throwable $e) {
report_fan_policy_exception($e);
}
$elapsed = microtime(true) - $started;
$sleep = max(0.05, FAN_POLICY_INTERVAL_SECONDS - $elapsed);
usleep((int)round($sleep * 1000000));
}
}
$daemon = in_array('--daemon', $argv ?? [], true);
if ($daemon) {
exit(run_fan_policy_daemon());
}
try {
exit(run_fan_policy_once() ? 0 : 1);
} catch (Throwable $e) {
report_fan_policy_exception($e);
exit(1); exit(1);
} }
+2 -2
View File
@@ -8,5 +8,5 @@ User=root
Group=root Group=root
WorkingDirectory=/var/www/control WorkingDirectory=/var/www/control
ExecStart=/usr/bin/php /var/www/control/bin/wifi_observe.php ExecStart=/usr/bin/php /var/www/control/bin/wifi_observe.php
StandardOutput=append:/var/log/control-wifi-observe.log StandardOutput=null
StandardError=append:/var/log/control-wifi-observe.log StandardError=journal
+19
View File
@@ -0,0 +1,19 @@
[Unit]
Description=Apply fan panel policy daemon
After=network-online.target mariadb.service
Wants=network-online.target
StartLimitIntervalSec=0
[Service]
Type=simple
User=root
Group=root
WorkingDirectory=/var/www/control
ExecStart=/usr/bin/php /var/www/control/apply_policy.php --daemon
Restart=always
RestartSec=3
StandardOutput=null
StandardError=journal
[Install]
WantedBy=multi-user.target