324 lines
36 KiB
Markdown
324 lines
36 KiB
Markdown
# Control
|
|
|
|
Raspberry Pi와 Synology 상태를 한 화면에서 보고, 팬 제어와 네트워크 상태 확인까지 처리하는 개인 운영용 시스템 대시보드입니다.
|
|
|
|
## 프로젝트 소개
|
|
|
|
Control은 라즈베리파이/리눅스 호스트의 팬과 시스템 상태를 웹에서 관리하고, 라즈베리파이 정품 터치 디스플레이에서는 별도 Qt 패널로 핵심 지표를 실시간 확인하기 위한 프로젝트입니다.
|
|
|
|
상태 갱신은 WebSocket을 우선 사용하고, 연결 실패 시 HTTP fallback으로 전환합니다. 팬 정책 적용과 센서 수집은 백그라운드 daemon과 API 호출을 통해 DB에 기록됩니다.
|
|
|
|
## 기술 스택
|
|
|
|
- **Backend:** `PHP`, `MariaDB`, `WebSocket`
|
|
- **Touch Panel:** `Python`, `PyQt`
|
|
- **System:** `Linux`, `Raspberry Pi`, `Synology`, `systemd`, `SSH`
|
|
- **Frontend:** `HTML`, `CSS`, `JavaScript`, `Chart.js`
|
|
|
|
## 주요 기능
|
|
|
|
- 팬 모드 `auto`, `manual`, `off` 제어
|
|
- PWM slider 기반 수동 팬 제어
|
|
- 서울 HA의 휴대폰 device tracker로 외출/귀가를 감지해 상태 변화 시 1회만 팬 모드 자동 전환
|
|
- 오버레이 설정 모달에서 보안 정책, 팬 자동곡선, 이벤트 판정, 팬 이상감지 파라미터 조정
|
|
- 설정 모달에서 라즈베리파이 정품 7인치 DSI 터치 디스플레이 상태 확인, 밝기/백라이트 즉시 적용, 콘솔 회전, 터치 좌표 보정, LCD/터치 비활성화 부팅 설정 조정
|
|
- CPU 온도, RP1 온도, CPU 전력, 팬 RPM 센서 이력 차트
|
|
- DHCP client 목록 아래에서 WAN, LAN, 2.4G WiFi, 5G WiFi 기준 다운로드/업로드 속도, RX/TX 패킷, 오류/드롭 이력 차트 표시
|
|
- UPS/배터리 잔여 시간 기능은 장치 제거 후 비활성 상태로 두고, 배터리 센서 조회와 장기 학습 계산은 돌리지 않음
|
|
- 라즈베리파이 저전압/스로틀링 현재 상태, 복구 중/반복 발생 판정, 부팅 후 이력, 최근 감지 시각, 지속시간, 최근 10분 통계 표시
|
|
- WiFi/LAN client 목록과 2.4G/5G/LAN client 수 표시
|
|
- 5G 외부 WiFi 모듈이 연결 시간을 제공하지 않는 경우 서버 관측 기반 연결 시간 보정
|
|
- WiFi 호스트명이 비어 있으면 MAC 기반 임시명을 표시하고, MAC별 수동 호스트명을 DB에 저장해 이후에도 유지
|
|
- WiFi 신호/송신 속도/수신 속도가 비어 있으면 대역, dBm, 송수신 속도, 5G RTL8822BU 특성을 기준으로 결측값 보정
|
|
- WiFi client 신호는 7단계 한글 등급으로 표시하고, hover/focus/touch 시 커스텀 툴팁에서 실제 dBm을 표시
|
|
- System Notice 최근 이력 표시
|
|
- 저전압/스로틀링 반복, 디스크 사용률, 최근 네트워크 오류/드롭 기준 System Notice 조건
|
|
- process CPU/MEM 후보는 프로세스 상세 접힘 영역에서 표시
|
|
- `/etc/systemd/system/*.service` 기준 사용자 생성 서비스의 서비스명, 현재 상태, enable 상태, PID, restart 횟수, 최근 journal 로그를 진단 접힘 영역에서 표시
|
|
- 사용자 서비스 로그는 서비스별 최근 500줄까지 조회하고, 사용자가 서비스 목록을 스크롤하는 동안에는 자동 갱신이 화면 위치를 되돌리지 않습니다.
|
|
- 영구 journal과 커널 크래시 보존 상태를 진단 접힘 영역에서 표시
|
|
- dmesg 로그는 진단 접힘 영역에서 필요할 때 열람
|
|
- WakeLock 버튼으로 대시보드 화면 꺼짐 방지
|
|
- Reboot 버튼으로 2단계 확인 후 시스템 재부팅 요청
|
|
- 기본 브라우저 alert/confirm/prompt 대신 대시보드 디자인에 맞춘 custom dialog 사용
|
|
- custom dialog는 Enter/Escape 키 입력을 자체 처리하며, Reboot 확인 흐름은 중복 실행을 차단
|
|
- Translate 버튼으로 `en`, `ko` UI 언어 토글
|
|
- Theme 버튼으로 `dark`, `light` UI 테마 토글
|
|
|
|
## 주요 API
|
|
|
|
- `public/api.php?action=status`: 팬, 센서, 시스템, WiFi, history, notice 상태 조회
|
|
- `public/api.php?action=collect`: 센서 snapshot 수집 후 팬 정책 적용
|
|
- `public/api.php?action=fan`: 팬 모드와 PWM 저장/적용
|
|
- `public/api.php?action=wifi`: 허용된 WiFi/DHCP service 제어
|
|
- `public/api.php?action=wifi_alias`: MAC별 WiFi 호스트명 저장
|
|
- `public/api.php?action=wake_lan_status`: LAN 클라이언트 WOL 대상 상태 조회
|
|
- `public/api.php?action=wake_lan`: LAN 클라이언트에 WOL magic packet 전송
|
|
- `public/api.php?action=settings`: 설정 가능한 Control 동작값 조회
|
|
- `public/api.php?action=settings_save`: 설정 모달 값 저장
|
|
- `public/api.php?action=settings_reset`: 설정 모달 값을 기본값으로 초기화
|
|
- `public/api.php?action=touch_display_save`: `/boot/firmware/config.txt`, `/boot/firmware/cmdline.txt`에 DSI 터치 디스플레이 부팅 설정 저장
|
|
- `public/api.php?action=touch_display_runtime_save`: DSI 터치 디스플레이 백라이트 밝기와 백라이트 전원 즉시 적용
|
|
- `public/api.php?action=dmesg`: dmesg 로그 조회
|
|
- `public/api.php?action=reboot`: 확인 단어와 관리자 암호 재검증 후 sudo reboot 실행
|
|
|
|
## 구성
|
|
|
|
- `public/index.php`: 로그인과 관리 화면
|
|
- `public/api.php`: 상태 조회와 조작 API
|
|
- `public/asset-version.php`: 화면과 API 파일 묶음의 현재 hash를 내려주는 JSON 엔드포인트
|
|
- `public/assets/asset-reload.js`: 열린 브라우저에서 파일 묶음 hash가 달라졌는지 확인하고 새로 여는 스크립트
|
|
- `public/assets/app.js`: 대시보드 렌더링, WebSocket, 차트, 조작 이벤트
|
|
- `public/assets/wakelock.js`: Screen Wake Lock API 제어
|
|
- `config/config.php`: DB, 인증, CSRF, shell 실행 공통 함수
|
|
- `apply_policy.php`: CLI 1회 실행과 daemon 모드를 지원하는 팬 정책 적용 루프
|
|
- `bin/control_ws.php`: WebSocket 서버. 핵심 PHP 파일 변경 감지 시 종료되고 systemd가 재시작해 새 코드를 로드
|
|
- `bin/wifi_observe.php`: 5G WiFi 연결 시간 보정을 위한 시스템단 관측 CLI
|
|
- `touch_panel/panel.py`: 라즈베리파이 정품 터치 디스플레이용 standalone Qt 대시보드
|
|
- `touch_panel/rpi_disk_temp_cache.py`: SSD 온도를 몇 초 단위로 읽어 `/run/control-rpi-disk-temp`에 캐시하는 터치 패널 보조 서비스
|
|
- `touch_panel/control_touch_bridge.php`: 이전 Qt 패널용 로컬 JSON 브리지. 현재 standalone 패널은 사용하지 않습니다.
|
|
- `touch_panel/control-touch-panel.desktop`: GUI 로그인 후 Qt 패널을 띄우는 데스크톱 autostart 파일
|
|
- `systemd/control-rpi-disk-temp.service`: 터치 패널용 SSD 온도 캐시 서비스 예시
|
|
- `systemd/fanpanel-apply.service`: 팬 정책을 1초 간격으로 계속 실행하는 daemon unit 예시
|
|
- `systemd/control-wifi-observe.*`: WiFi 관측 CLI를 주기적으로 실행하는 systemd unit 예시
|
|
- `systemd/control-touch-panel.service`: GUI 세션에서 Qt 관리자 패널을 띄우는 systemd unit 예시
|
|
|
|
`asset-version.php`는 `public`의 PHP/JS/CSS/manifest/icon 파일과 `config`의 PHP 파일을 읽어 크기, 수정 시각, sha256 hash를 묶은 전체 버전과 파일별 버전을 만듭니다. `index.php`는 `app.js`, `asset-reload.js`, `wakelock.js`, manifest, icon 경로에 실제 파일 수정 시각과 hash 기반 `v` 값을 붙여 정적 파일 캐시가 오래 남지 않게 합니다. `asset-reload.js`는 화면 복귀, 포커스 복귀, 온라인 복귀, 모바일 터치/클릭 복귀, 30초 간격으로 이 값을 다시 확인하고, 전체 버전 또는 현재 로드된 script/link의 파일별 `v` 값이 다르면 브라우저 Cache Storage를 비우고 service worker 등록을 해제한 뒤 현재 화면을 다시 엽니다. 현재 URL에 최신 `assetReload` 토큰이 이미 있으면 파일별 `v` 값이 낡아 보여도 반복해서 다시 열지 않습니다. 삼성 브라우저 등에서 Web Storage나 `Promise.finally()` 호환성이 부족해도 동작하도록 쿠키 fallback과 timeout 기반 캐시 삭제를 사용합니다. `index.php`는 `no-store` 헤더를 내려 문서 자체가 브라우저 캐시에 오래 남지 않게 합니다.
|
|
|
|
## 데이터/저장소
|
|
|
|
- `control_state`: 팬 모드와 PWM 상태
|
|
- `presence_fan_policy_state`: 외출/귀가 기반 팬 정책의 마지막 확인/적용 상태
|
|
- `sensor_logs`: 온도, RPM, PWM, load, memory, disk, uptime. 과거 배터리 컬럼은 호환을 위해 유지하지만 UPS 제거 상태에서는 새 배터리값을 기록하지 않습니다.
|
|
- `network_usage_logs`: WAN, LAN, 2.4G WiFi, 5G WiFi 기준 byte/packet/error/drop 커널 카운터와 초당 다운로드/업로드 속도, RX/TX 패킷 이력
|
|
- `network_quality_totals`: 인터페이스별 RX/TX 오류와 드롭 누적 개수. 커널 카운터가 재부팅으로 초기화되면 현재 값을 새 증가분으로 보고 DB 누적값에 더합니다.
|
|
- `power_event_states`: 저전압/스로틀링 episode 상태, 최근 지속시간, 최근 이벤트 이력
|
|
- `battery_profile_cache`: 배터리 잔여시간 계산용 장기 프로파일 캐시
|
|
- `systemd_service_cache`: 사용자 생성 systemd 서비스 목록과 최근 journal 로그의 짧은 TTL 캐시
|
|
- `system_notice_state`: notice 활성 상태와 기준값
|
|
- `system_notice_logs`: notice 발생 이력
|
|
- `wifi_observed_sessions`: 5G WiFi/LAN client의 최초/마지막 감지 시간
|
|
- `wifi_client_aliases`: MAC별 수동 WiFi 호스트명
|
|
- `app_settings`: 설정 모달에서 저장한 Control 동작값 오버라이드
|
|
- `/home/seo/secret/control.php`: 앱 비밀번호, DB 설정, 선택적 배터리 설정, WOL 전용 API 토큰
|
|
- UPS/배터리 기능은 `/home/seo/secret/control.php`의 `battery.enabled`가 명시적으로 true일 때만 동작합니다. 현재 운영 기준은 UPS 제거 상태이므로 기본값은 false입니다.
|
|
|
|
## 로그 보존
|
|
|
|
커널 패닉이나 강제 재부팅 분석을 위해 `systemd-journald`는 `/var/log/journal`에 영구 저장합니다. `dtoverlay=ramoops` 기반 pstore는 부팅 직후 `control-pstore-collect.service`가 `/var/log/pstore`로 복사합니다. 현재 pstore에 남은 크래시 조각이 없으면 이 서비스는 조건 불충족으로 `inactive`일 수 있으며 정상입니다.
|
|
|
|
## 시놀로지 팬 가드
|
|
|
|
시놀로지 CPU 온도는 `k10temp`의 `Tdie` 값을 기준으로 보고, `nct6795`의 실제 회전 팬 `fan2_input`과 `pwm2`를 감시합니다. `bin/synology_fan_guard.sh`는 DSM 기본 팬 제어를 평소에는 유지하고, CPU가 75도 이상이면 `pwm2`를 255로 강제합니다. CPU가 65도 이하로 내려가고 수동 강제 상태라면 DSM 자동제어(`pwm2_enable=5`)로 되돌립니다. systemd 예시는 `systemd/synology-fan-guard.service`에 둡니다.
|
|
|
|
## UPS/배터리 상태
|
|
|
|
UPS가 과부하 상황에서 전압 하강과 강제 종료를 반복해 현재 운영 구성에서는 제거되어 있습니다. 이에 따라 Control은 기본적으로 MAX17048 배터리 센서를 읽지 않고, 배터리 SOC/전압 차트와 잔여 시간 UI도 표시하지 않습니다.
|
|
|
|
API 응답의 `battery.enabled`는 false, `battery.removed`는 true로 내려가며 `battery.remaining.display`는 `UPS 제거됨`으로 표시됩니다. 과거에 저장된 `sensor_logs`의 배터리 컬럼은 DB 호환을 위해 남겨 두지만 새 snapshot에서는 배터리값을 추가하지 않습니다.
|
|
|
|
나중에 UPS 구성을 다시 붙일 경우 `/home/seo/secret/control.php`의 `battery.enabled`를 true로 두면 기존 배터리 측정/학습 코드가 다시 사용됩니다.
|
|
|
|
## 설정 모달
|
|
|
|
상단 `설정` 버튼은 전체 화면 오버레이 모달을 엽니다. 설정값은 `app_settings` 테이블에 저장되며, 저장 직후 다음 snapshot부터 반영됩니다. 민감정보가 들어 있는 `/home/seo/secret/control.php`는 직접 수정하지 않고 DB 오버라이드만 사용합니다.
|
|
|
|
설정 가능한 항목은 다음 범위입니다.
|
|
|
|
- 팬 자동 제어: 팬 시작 온도, 최대 온도, 즉시 최대 PWM 온도, RP1 온도 반영 비율, 팬 판단 온도 여유, 자동 상승/하강 PWM step, 외출/귀가 팬 모드 자동 전환, 외출 시 PWM
|
|
- 보안 정책: 자동 로그인 유지 기간, remember 쿠키 Secure/SameSite, CSRF 길이, remember 토큰 길이, User-Agent/IP 접두사 검증, 로그인 실패 잠금, 재부팅 허용 여부, 재부팅 확인 문구, 명령 timeout, WiFi 조작 허용 여부
|
|
- 화면/진단 표시: 프로세스 후보 수, 사용자 서비스 로그 줄 수, 사용자 서비스 캐시 시간, 팬 이상 이력 수
|
|
- 터치 디스플레이: 현재 DSI 연결 상태, `800x480` 모드, framebuffer, 백라이트 밝기, 자동감지/수동 overlay 상태 표시와 밝기 즉시 적용, 콘솔 회전, LCD 출력, 터치 입력, `invx`/`invy`/`swapxy`, 터치 좌표 크기 저장
|
|
- 저전압/스로틀링: 최근 판정창, 복구 중 유지 시간, 반복 발생 기준 횟수
|
|
- 팬 이상감지: 기준 표본 수, 최신 표본 제외 수, RPM/온도 이상 감지 차이, RPM/온도 복구 차이, RPM 감시 시작 PWM, alert 유지 중 반복 기록, 반복 기록 간격, 하강 변화 원인 후보 표시 여부
|
|
- UPS/배터리 설정: 현재 UPS 제거 상태에서는 설정 모달에서 숨겨지고, `battery.enabled=true`일 때만 배터리 예측 항목을 노출합니다.
|
|
|
|
디스플레이 회전/터치 좌표 설정은 DB가 아니라 라즈베리파이 부팅 파일에 저장합니다. 저장 전 기존 파일을 `.control-YYYYMMDD-HHMMSS.bak` 형식으로 백업하고, 변경 사항은 재부팅 후 적용됩니다. 구형 정품 7인치 Touch Display의 콘솔 회전은 `cmdline.txt`의 `video=DSI-1:800x480@60,rotate=...`를 사용하며, 터치 좌표 보정이 필요하면 `config.txt`의 `vc4-kms-dsi-7inch` overlay로 전환합니다. 백라이트 밝기와 백라이트 전원은 `/sys/class/backlight` 장치를 통해 슬라이더/토글 조작 직후 debounce 후 즉시 적용되며 재부팅이 필요 없습니다. 백라이트 전원을 끄면 brightness도 0으로 함께 맞춰 상태 일관성을 유지합니다.
|
|
|
|
설정 모달이 열려 있는 동안 1초 상태 갱신은 폼 DOM을 다시 만들지 않습니다. 입력 중인 값은 저장, 기본값 초기화, 닫기 전까지 유지되며, 백그라운드 snapshot은 내부 설정 payload만 갱신합니다. 단순 boolean 설정은 `켜기`/`끄기` 세그먼트로 표시합니다.
|
|
|
|
## 터치 패널 대시보드
|
|
|
|
라즈베리파이 정품 7인치 DSI 터치 디스플레이에 띄울 로컬 Qt 대시보드를 둡니다. 현재 GUI 패키지와 `lightdm`이 설치되어 있고 기본 부팅 target은 `graphical.target`입니다. `seo` GUI 세션의 데스크톱 autostart로 패널을 띄웁니다.
|
|
|
|
```bash
|
|
sudo raspi-config
|
|
sudo systemctl set-default graphical.target
|
|
```
|
|
|
|
패키지가 제거된 상태면 Raspberry Pi OS 데스크톱 패키지와 X 서버를 먼저 복구해야 합니다.
|
|
|
|
수동 실행은 GUI 세션에서 다음처럼 합니다.
|
|
|
|
```bash
|
|
cd /var/www/control
|
|
/usr/bin/python3 touch_panel/panel.py
|
|
```
|
|
|
|
패널은 Control PHP/API/DB snapshot을 호출하지 않는 standalone 방식입니다. Python이 `/sys/class/net`, `/proc/diskstats`, `/sys/class/thermal`, 팬 hwmon 파일을 직접 읽습니다. 상태패널의 네트워크/I/O 표시와 UPLINK 트래픽 그래프는 100ms 간격으로 갱신하고, 팬 카드와 온도/RPM 그래프는 500ms 간격으로 갱신합니다. 왼쪽 상태패널은 `RASPBERRY`, `SYNOLOGY`, `CHECK` 3열이며, 라즈베리파이 순서는 `SSD I/O`, `LOCAL`, `UPLINK`, `ETH1`, `5G`, `2.4G`입니다. 시놀로지 열은 `PING`, 정상 디스크 `SATA1 I/O`, `SATA2 I/O`, `SATA4 I/O`, `LOCAL`, `UPLINK`를 표시합니다. `CHECK` 열은 `RPI TEMP`, `RPI FAN`, `RPI THROT`, `SYNO TEMP`, `SYNO FAN`, `SYNO GUARD`를 표시합니다. `RPI TEMP`와 `SYNO TEMP`는 80도 이상부터 빨간 표시등을 500ms 간격으로 점멸하고, 팬 카드는 RPM 0 또는 7000 초과를 빨강, 4500 초과를 주황으로 표시합니다. 시놀로지 `PING`은 장기 실행 `ping -i 0.5` 출력 파싱으로 읽고, 15ms 이하 초록, 30ms 이하 주황, 50ms 이하 빨강, 50ms 초과 빨강 500ms 점멸입니다. `RPI THROT`은 정상 초록, 부팅 후 이력 주황, 현재 언더볼트 빨강, 현재 스로틀 빨강 500ms 점멸, 현재 언더볼트와 스로틀 동시 발생 빨강 100ms 점멸로 표시합니다. `SYNO GUARD`는 `synology-fan-guard.service`가 active이고 DSM 자동제어면 초록 `AUTO`, 고온으로 PWM 최대 강제 중이면 주황 `FULL`, 서비스 실패나 팬 RPM 0이면 빨강 `FAIL`로 표시합니다. `LOCAL`은 `lo`, `UPLINK`는 기본 라우트 인터페이스 기준입니다. 라즈베리파이 `ETH1`은 컴퓨터와 연결된 물리 포트 단독 카운터이며 `br0`는 합산하지 않습니다. 시놀로지는 SSH를 유지한 채 100ms마다 디스크별 sector I/O, `LOCAL`, `UPLINK` 카운터만 스트림으로 가져옵니다. SATA SMART/온도 직접 조회는 응답 지연 위험 때문에 대시보드에서 실행하지 않고, 각 SATA 카드에는 `조회 중지`를 표시합니다. 라즈베리파이 CPU/SSD 온도와 시놀로지 CPU 온도/FAN RPM은 500ms 간격으로 캐시해 표시합니다. 왼쪽 표시등은 장식용 heartbeat가 아니라 직전 샘플 대비 현재 카운터 증가량이 0 초과인지에 반응합니다. 각 타일에는 글자 아래에 세 개의 작은 표시등을 두며 읽기/업로드은 초록, 쓰기/다운로드는 주황, 오류/드롭 증가는 빨강으로 표시합니다. 활동 표시등은 이벤트 감지 후 120ms 동안 유지하고, 오류 표시등은 2초 동안 유지해 너무 짧은 이벤트도 눈에 보이게 합니다. 네트워크 카드 오른쪽 값은 항상 `D n`으로 드롭 누적을 표시합니다. `SSD I/O`의 값은 `/run/control-rpi-disk-temp` 캐시 파일의 `/dev/sda` Samsung T7 디스크 자체 온도입니다. 캐시 파일은 root로 실행되는 `control-rpi-disk-temp.service`가 몇 초마다 `smartctl -d sntasmedia`로 갱신하므로 GUI 패널은 sudo를 반복 실행하지 않습니다. 네트워크 상태값은 정상, 드롭 누적, 꺼짐으로 표시합니다. 상태패널 최하단의 `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, 라즈베리파이 UPLINK 트래픽, 시놀로지 온도/RPM, 시놀로지 UPLINK 트래픽 그래프를 순서대로 표시합니다. 온도/RPM 그래프는 최근 60초 기준 500ms 샘플을 사용하고, UPLINK 트래픽 그래프는 100ms 샘플과 200포인트 기준으로 표시합니다. 트래픽 속도와 그래프 Y축은 byte/sec 기준으로 `B/s`, `KB/s`, `MB/s`, `GB/s` 중 자동 변환하고, 다운로드 주황/업로드 초록 두 선을 같은 축 범위에 맞춰 그립니다. 그래프 우측 상단 현재값은 고정폭으로 표시합니다.
|
|
|
|
GUI 자동 실행은 데스크톱 autostart가 1차 기준입니다.
|
|
|
|
```bash
|
|
mkdir -p /home/seo/.config/autostart
|
|
cp /var/www/control/touch_panel/control-touch-panel.desktop /home/seo/.config/autostart/
|
|
```
|
|
|
|
systemd 방식이 필요하면 `systemd/control-touch-panel.service`를 `/etc/systemd/system/`에 복사해 씁니다.
|
|
|
|
## 갱신 주기
|
|
|
|
- WebSocket 상태 갱신: 1초마다
|
|
- WebSocket 최초 연결: 즉시 1회 상태 전송
|
|
- WebSocket 끊김 시 HTTP fallback 상태 갱신: 2초마다
|
|
- 탭 복귀: 즉시 1회 상태 갱신
|
|
- UPS/배터리 잔여 시간: 현재 운영 구성에서는 표시하지 않음
|
|
- dmesg: 진단 패널에서 열었을 때만 1초마다 갱신, 닫으면 중지
|
|
- 팬 정책 daemon: `fanpanel-apply.service`가 `apply_policy.php --daemon`을 실행해 1초마다 팬 정책과 센서 로그를 처리
|
|
- WiFi 관측 보정 timer: 10초마다 `bin/wifi_observe.php` 실행
|
|
- 외출/귀가 팬 정책: 팬 정책 적용 루프에서 기본 30초마다 서울 HA의 휴대폰 device tracker를 확인
|
|
- WebSocket 소스 변경 감지: 15초마다 확인 후 재시작
|
|
- 팬 슬라이더 자동 적용 debounce: 약 450ms
|
|
|
|
## System Notice 구조
|
|
|
|
Control은 급격한 팬/온도 변화를 외부 푸시로 보내지 않고 `system_notice_logs`에 로컬 이력으로 저장합니다. 대시보드는 최근 이력을 진단 영역에서 보여주며, 배터리 SOC 낮음 같은 모바일 푸시 발송은 더 이상 수행하지 않습니다.
|
|
|
|
### 시스템 알림 조건
|
|
|
|
System Notice는 다음 조건으로 동작합니다.
|
|
|
|
- System Notice 시작: 팬 모드가 `off`가 아닐 때, 최근 안정 기준선 대비 팬 RPM 차이가 `팬 RPM 이상 감지 차이` 이상이거나 온도 차이가 `온도 이상 감지 차이` 이상이면 alert로 진입합니다. RPM 감시는 현재 PWM 또는 기준 PWM이 `RPM 감시 시작 PWM` 이상일 때만 의미 있는 변화로 봅니다.
|
|
- System Notice 기준선: normal 상태에서는 최근 sensor history에서 최신 몇 개 표본을 제외하고 절사 평균으로 기준선을 계속 갱신합니다. alert로 들어가면 진입 당시 온도/RPM/PWM 기준선을 고정해, alert 중 기준선이 문제 상태를 따라가며 사라지는 일을 막습니다.
|
|
- System Notice 복구: 고정 기준선 또는 최신 rolling 기준선 대비 RPM/온도 차이가 각각 복구 차이 이하로 내려가면 normal로 돌아갑니다. 팬 모드가 `off`이면 fan off 기준선을 별도로 고정하고 System Notice alert를 만들지 않습니다.
|
|
- System Notice 기록: alert 신규 진입 시 `system_notice_logs`에 기록합니다. `Alert 유지 중 반복 기록`을 켜면 유지 중에도 설정한 반복 기록 간격마다 다시 기록할 수 있습니다.
|
|
- 저전압/스로틀링 표시: `vcgencmd get_throttled`의 현재 비트와 부팅 후 이력 비트를 읽어 현재 상태, 최근 감지, 지속시간, 최근 10분 통계를 표시합니다. 이 값은 System Notice와 별개이며, 팬/온도 alert 조건에는 직접 섞지 않습니다.
|
|
- 추가 Notice 조건: 설정값에 따라 저전압/스로틀링 반복, 디스크 사용률, 최근 네트워크 오류/드롭 증가량도 System Notice 이력에 함께 기록합니다.
|
|
|
|
## WiFi 관측 자동화
|
|
|
|
5G 외부 WiFi 모듈과 LAN 클라이언트는 장치별 연결 시간이 직접 제공되지 않는 경우가 있어, 서버가 MAC별 최초 감지 시각을 DB에 저장하고 경과 시간을 계산합니다. DHCP 클라이언트 목록은 연결 시간이 가장 긴 장치부터 표시합니다.
|
|
|
|
기존 대시보드 조회만으로도 보정은 가능하지만, 사용자가 접속하지 않은 시간에는 최초 감지가 늦어질 수 있습니다. 이를 막기 위해 다음 systemd timer가 10초마다 관측 CLI를 실행합니다.
|
|
|
|
```bash
|
|
control-wifi-observe.timer
|
|
control-wifi-observe.service
|
|
```
|
|
|
|
실행 명령은 다음과 같습니다.
|
|
|
|
```bash
|
|
/usr/bin/php /var/www/control/bin/wifi_observe.php
|
|
```
|
|
|
|
이 작업은 팬 정책, 차트 로그 저장, 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`에 보관해 이후에도 우선 표시합니다.
|
|
|
|
신호/송신 속도/수신 속도는 실제 `iw station dump` 값이 있으면 그대로 사용합니다. 값이 비어 있으면 2.4G는 일반 802.11n 계열 링크 속도와 dBm 범위를 기준으로, 5G는 Realtek RTL8822BU/802.11ac 계열 동글의 최대 1300Mbps 특성을 참고해 dBm과 링크 속도 사이를 보정합니다. 화면의 신호 칸은 숫자 대신 `최상`, `매우 좋음`, `좋음`, `양호`, `보통`, `약함`, `매우 약함` 7단계 등급으로 표시하고, 마우스 hover/키보드 focus/모바일 touch 시 커스텀 툴팁으로 실제 dBm을 표시합니다. 툴팁 내용과 위치는 1초 상태 렌더마다 다시 갱신됩니다.
|
|
|
|
## 처리 흐름
|
|
|
|
1. 화면 진입 시 로그인 세션과 remember token을 확인합니다.
|
|
2. Rhymix 사이트 관리자 세션 또는 `/custom/common` 관리자 인증 쿠키가 확인되면 Control 로그인 화면을 생략하고 내부 세션을 발급합니다.
|
|
3. 대시보드는 `status` snapshot을 렌더링합니다.
|
|
4. WebSocket 연결이 성공하면 `status` 메시지로 갱신하고, 실패하면 HTTP fallback을 사용합니다.
|
|
5. 팬 조작은 상태 저장, 정책 적용, 로그 저장 순서로 처리합니다.
|
|
6. WiFi client 목록은 `iw station dump`와 dnsmasq lease를 조합하고, LAN client는 `bridge fdb show br br0`의 USB LAN 포트 MAC을 dnsmasq lease와 매칭합니다. 5G 또는 LAN 연결 시간이 비어 있으면 MAC 기준 최초 감지 시간을 DB에 저장해 경과 시간을 계산합니다. LAN client의 속도 칸은 무선 송수신 속도 대신 USB LAN 인터페이스의 현재 협상 링크 속도를 표시합니다.
|
|
7. WiFi hostname이 비어 있으면 MAC 기반 임시명을 표시하고, 수동 저장한 MAC별 이름은 DB에서 우선 적용합니다.
|
|
8. WiFi 신호/송신 속도/수신 속도 값이 비어 있으면 실제로 존재하는 다른 무선 지표와 대역별 기준으로 값을 채웁니다.
|
|
9. `control-wifi-observe.timer`는 사용자 접속과 무관하게 10초마다 5G/LAN 관측 세션을 갱신합니다.
|
|
10. WakeLock 버튼은 활성 상태를 초록색 버튼으로 표시합니다.
|
|
11. Reboot 버튼은 설정된 확인 문구와 관리자 암호 재입력을 모두 통과한 뒤 서버 API로 재부팅을 요청합니다.
|
|
12. 시스템 상태는 `vcgencmd get_throttled`를 읽어 현재 저전압/스로틀링 여부와 부팅 후 이력, 최근 감지 시각, 현재/최근 지속시간을 표시합니다.
|
|
13. 저전압/스로틀링 상태 파일에는 최근 episode 시작/종료를 보관하고, 최근 10분 발생 횟수, 감지 누적시간, 감지 비율을 계산합니다.
|
|
14. UPS/배터리 장치가 제거되어 배터리 센서 조회와 잔여 시간 학습 계산은 기본적으로 돌리지 않습니다.
|
|
15. System Notice 조건이 맞으면 Control이 `system_notice_logs`에 이력을 저장합니다.
|
|
16. 대시보드 첫 화면에는 팬/전원/스로틀링/WiFi/핵심 차트를 우선 배치하고, 상세 차트, 프로세스 후보, 사용자 서비스, dmesg는 접힘 영역으로 분리합니다.
|
|
|
|
## 팬 판단 온도
|
|
|
|
화면의 CPU 온도 표시는 기존처럼 CPU 센서 값을 보여줍니다. 자동 팬 제어는 CPU만 보지 않고 RP1 온도도 함께 반영한 `fan_control_temp()` 값을 사용합니다.
|
|
|
|
계산은 CPU 온도와 RP1 온도를 설정된 `RP1 온도 반영 비율`로 섞은 뒤 `팬 판단 온도 여유`를 더하고, 이 값과 CPU 온도 중 더 높은 값을 팬 판단 온도로 삼습니다. 기본값은 RP1 35%, 여유 1.5도입니다. RP1 값이 없으면 CPU 온도만 사용합니다.
|
|
|
|
이 구조는 팬 하나가 CPU와 RP1 주변 열을 같이 빼는 현실을 반영하되, 화면의 CPU 표기와 차트 의미는 흐리지 않도록 팬 제어 내부 값만 따로 둡니다. API 응답에는 `fan.control_temp_c`와 `system.fan_control_temp_c`가 함께 내려갑니다.
|
|
|
|
## 주요 함수/모듈
|
|
|
|
- `collect_snapshot()`: 센서와 fan 상태 snapshot 생성
|
|
- `custom_systemd_services()`: `/etc/systemd/system/*.service` 단위를 조회해 사용자 생성 서비스 상태와 최근 journal 로그를 구성하고 `systemd_service_cache`에 짧은 TTL 캐시로 저장
|
|
- `systemd_service_logs()`: 서비스별 최근 journal 로그 조회
|
|
- `read_throttled_flags()`: `vcgencmd get_throttled`를 직접 조회하고, 권한 문제로 실패하면 sudo 경유 조회를 시도
|
|
- `throttled_event_status()`: 저전압/스로틀링 episode 이력, 복구 중/반복 발생 판정, 최근 10분 통계를 계산
|
|
- `throttled_statuses()`: 라즈베리파이 throttled flag를 읽고 저전압/스로틀링 감지 이력을 `power_event_states`로 추적
|
|
- `apply_fan_policy()`: 팬 목표값 계산과 적용
|
|
- `fan_control_temp()`: CPU/RP1 혼합 팬 판단 온도 계산
|
|
- `json_out()`: API JSON 응답 표준화
|
|
- `apply_observed_wifi_connected_time()`: 5G WiFi와 LAN 연결 시간이 없는 client에 서버 관측 경과 시간 적용
|
|
- `wifi_client_aliases()`, `save_wifi_client_alias()`: MAC별 WiFi 호스트명 조회와 저장
|
|
- `apply_wifi_estimates()`: WiFi 신호/송신 속도/수신 속도 결측값 보정
|
|
- `battery_status()`: `CONTROL_BATTERY_ENABLED`가 true일 때만 배터리 센서를 읽고, 현재 기본 구성에서는 제거 상태를 반환
|
|
- `battery_trend_history()`: 배터리 기능을 다시 켤 때 최근 24시간 배터리 기록을 1분 단위로 집계
|
|
- `battery_profile_history()`: 배터리 기능을 다시 켤 때 최대 45일 배터리 기록을 약 5분 간격으로 샘플링하고 `battery_profile_cache`에 보관
|
|
- `weighted_linear_regression()`, `numeric_mad()`: SOC 기울기 계산과 회귀 잔차 튐 제거
|
|
- `battery_regression_trend_candidate()`: 1분 SOC 집계를 강건 회귀로 분석해 정밀 방전 속도 후보 계산
|
|
- `battery_voltage_trend_candidate()`: 전압 기울기와 동적 하한 전압으로 잔여 시간 후보 계산
|
|
- `battery_energy_profile_candidate()`: 장기 기록에서 SOC 1%당 실제 사용 Wh를 학습해 잔여 시간 후보 계산
|
|
- `battery_capacity_power_candidate()`: 설정 배터리 용량과 최근 전력으로 용량 기반 후보 계산
|
|
- `battery_runtime_cap_seconds()`, `battery_cap_candidate()`, `battery_cap_result()`: SOC 비례 현실 상한과 상한 초과 후보 감쇠 적용
|
|
- `refine_battery_candidates()`: 후보 방전 속도의 중앙값에서 크게 벗어난 값을 제외하거나 가중치 축소
|
|
- `battery_learned_profile_candidate()`: 누적 기록에서 SOC/부하 구간별 장기 방전 프로파일 후보 계산
|
|
- `battery_remaining_estimate()`: SOC 다중 시간창 방전 추세, 장기 학습 프로파일, 현재 부하 보정으로 잔여 시간 계산
|
|
- `battery_power_fallback_estimate()`: SOC 추세가 부족할 때 배터리 용량과 최근 CPU 전력 평균으로 잔여 시간 대체 계산
|
|
- `sync_current_battery_remaining_history()`: 잔여 시간 차트 마지막 지점을 현재 snapshot 잔여 시간과 동기화
|
|
- `batteryRemainingTitle()`, `showBatteryTooltip()`, `updateBatteryTooltip()`: 잔여 시간 계산 근거 커스텀 팝오버 표시와 실시간 갱신
|
|
- `setting_definitions()`, `settings_payload()`, `save_settings()`, `reset_settings()`: 설정 모달 항목 정의, 조회, 저장, 초기화
|
|
- `touch_display_status()`, `touch_display_set_config()`: 정품 7인치 DSI 터치 디스플레이 인식 상태와 부팅 설정 조회/저장
|
|
- `bin/wifi_observe.php`: 화면 접속 없이 `wifi_data()`를 호출해 5G/LAN 관측 세션을 선제 갱신
|
|
- `assets/wakelock.js`: WakeLock 버튼 상태와 Screen Wake Lock API 제어
|
|
- `customAlert()`, `customConfirm()`, `customPrompt()`: 대시보드 공통 확인/입력 dialog
|
|
- `controlLang`, `controlTheme`: 재접속 후에도 유지되는 언어/테마 localStorage key
|
|
- `CONTROL_BATTERY_ENABLED`: UPS/배터리 센서와 잔여시간 학습 계산 사용 여부
|
|
- `BATTERY_CELL_CAPACITY_MAH`, `BATTERY_PARALLEL_CELLS`, `BATTERY_CAPACITY_MAH`, `BATTERY_NOMINAL_VOLTAGE`, `BATTERY_CAPACITY_WH`: 배터리 기능을 다시 켤 때 쓰는 병렬 배터리팩 용량과 잔여시간 계산 기준
|
|
|
|
## 보안
|
|
|
|
- 로그인 세션과 CSRF token을 사용합니다.
|
|
- `/custom/wakeonlan` 서버 프록시가 쓰는 `wake_lan`, `wake_lan_status`만 전용 API 토큰으로 호출할 수 있습니다. 이 토큰은 해당 두 액션에만 유효하며, 일반 POST 제어는 기존 로그인 세션과 CSRF token을 계속 요구합니다.
|
|
- Rhymix 사이트 관리자 세션 또는 `/custom/common` 관리자 인증 쿠키가 확인되면 Control 내부 로그인 세션을 발급합니다.
|
|
- Control 로그아웃은 Control remember token과 세션을 지우고, `/custom/common` 공통 인증 쿠키도 함께 만료합니다.
|
|
- POST 조작 API는 CSRF 검증을 통과해야 합니다.
|
|
- sysfs 쓰기와 systemctl 실행 권한은 sudoers 범위로 제한해야 합니다.
|
|
- 재부팅 API는 CSRF, 로그인 세션, 설정된 확인 문구, 관리자 암호 재검증을 모두 요구합니다.
|
|
- 로그인 화면은 CSRF 검사를 수행하고, 설정한 실패 횟수/집계 시간/잠금 시간에 따라 세션 단위 잠금을 적용합니다.
|
|
- Remember login은 설정에 따라 Secure/SameSite, 토큰 길이, 만료 기간, User-Agent 고정, IPv4 접두사 고정을 조정할 수 있습니다.
|
|
- WiFi restart/reload 허용 여부와 root 명령 timeout은 설정 모달에서 조정합니다.
|
|
- 앱 비밀번호와 DB/선택적 배터리 설정은 저장소 밖 secret 파일로 관리합니다.
|
|
|
|
## 운영 체크포인트
|
|
|
|
- 센서 수집 주기와 DB 증가량을 확인합니다.
|
|
- UPS/배터리를 다시 붙일 때는 `/home/seo/secret/control.php`의 `battery.enabled`와 용량값을 먼저 확인합니다.
|
|
- WiFi client 표는 갱신 중에도 가로 스크롤 위치를 보존합니다.
|
|
- 사용자 서비스 목록은 갱신 중에도 스크롤 위치를 보존하고, 스크롤/터치/휠 조작 직후에는 DOM 교체를 잠시 미룹니다.
|
|
- 하드웨어 또는 OS 변경 후 fan sysfs 경로를 확인합니다.
|
|
- AP 국가는 `PA`로 두고, `router-performance.service`가 WiFi power save를 끄고 출력 요청값을 `3600`으로 넣습니다. 실제 출력은 무선 모듈/드라이버가 허용하는 값에서 멈춥니다.
|
|
- 2.4G `hostapd-24g.conf`는 `max_num_sta=32`로 두어 hostapd station table 제한 가능성을 줄입니다. 실제 동시 접속 수는 내장 `brcmfmac` 펌웨어/드라이버 한계가 따로 걸릴 수 있습니다.
|
|
- 2.4G 내장 WiFi는 `hostapd-24g.service` 개별 restart 직후 일부 IoT 단말이 WPA/EAPOL 재협상 루프에 들어갈 수 있으므로, 안정화된 상태에서는 개별 restart를 피하고 필요 시 전체 reboot 또는 채널 변경으로 재초기화합니다.
|
|
- `hostapd-24g.service`, `hostapd-5g.service`는 종료 후 해당 `wlan` 인터페이스를 `br0`에서 떼도록 두어, 모듈이 빠졌다가 돌아온 뒤 hostapd가 다시 붙이는 흐름으로 정리합니다.
|
|
- 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 연결 시간 카운터를 시작하고 유지합니다.
|
|
- WebSocket은 장기 실행 프로세스이므로 `public/api.php`, `config/config.php`, `bin/control_ws.php` 변경을 감지하면 15초 안에 종료되고 `control-websocket.service`가 새 프로세스로 재시작합니다.
|
|
- WebSocket 장기 실행 중 DB 연결이 끊길 수 있으므로 reconnect 로그를 확인합니다.
|
|
- SmartThings 재로드 자동화는 서울 전력분전반과 목포 누전차단기 전력 센서의 값이 10초 이상 변하지 않으면 통합을 재로드합니다.
|
|
- Reboot API 사용 전 웹 서버 실행 계정의 sudoers에 `/usr/sbin/reboot` 비밀번호 없는 실행 권한이 제한적으로 설정되어 있는지 확인합니다.
|
|
- 저전압/스로틀링이 `N/A`로 보이면 웹 서버 실행 계정의 `/dev/vcio` 접근 권한과 `/usr/bin/vcgencmd get_throttled` sudoers 허용 여부를 확인합니다.
|
|
- 언어/테마 표시가 예상과 다르면 브라우저 localStorage의 `controlLang`, `controlTheme` 값을 확인합니다.
|
|
- 시놀로지 운영 상태 확인은 NFS 경로가 아니라 SSH와 HTTPS API를 기준으로 합니다. Control 런타임에서 필요한 공용 HA 설정은 `/home/seo/secret/control.php`의 `common` 항목에 둡니다.
|
|
- smartban 판단 로그는 로컬 `logger -t smartban`으로 남깁니다.
|