diff --git a/php/monthly_report.php b/php/monthly_report.php index 28862e2..2d398cd 100644 --- a/php/monthly_report.php +++ b/php/monthly_report.php @@ -1,10 +1,14 @@ format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')]; } -// SMS 메시지 태그(prefix) → 집계 키 매핑. 태그 문자열은 config.php 의 SMS_TAG_* -// 단일 출처 상수에서 가져온다(sms_send.php 가 발송 시 붙이는 태그와 자동 일치). -function report_sms_tags(): array { - return [ - 'high_temp' => SMS_TAG_HIGH_TEMP, - 'low_temp' => SMS_TAG_LOW_TEMP, - 'high_humidity' => SMS_TAG_HIGH_HUMIDITY, - 'low_humidity' => SMS_TAG_LOW_HUMIDITY, - 'recovery' => SMS_TAG_RECOVERY, - 'device_offline' => SMS_TAG_DEVICE_OFFLINE, - 'device_online' => SMS_TAG_DEVICE_ONLINE, - ]; -} - function round_or_null($value, int $precision = 1): ?float { return $value !== null ? round((float)$value, $precision) : null; } @@ -74,7 +64,7 @@ function csv_cell($value): string { $db = get_db(); [$month, $start_at, $end_at] = month_range($_GET['month'] ?? date('Y-m')); $report_generated_at = date('Y-m-d H:i:s'); -$report_scope = '온습도 임계 경보, 장비 오프라인, SMS'; +$report_scope = '온습도 임계 경보, 누수 감지, 장비 오프라인, SMS'; // ── 월간 요약: SMS 태그별 건수 + 실패 건수 ─────────────────────────────────── $stmt = $db->prepare(" @@ -94,6 +84,8 @@ $summary = [ 'recovery' => (int)($row['recovery'] ?? 0), 'device_offline' => (int)($row['device_offline'] ?? 0), 'device_online' => (int)($row['device_online'] ?? 0), + 'leak_alert' => (int)($row['leak_alert'] ?? 0), + 'leak_recovery' => (int)($row['leak_recovery'] ?? 0), 'sms_fail' => (int)($row['sms_fail'] ?? 0), 'metrics' => 0, 'avg_temp' => null, @@ -130,12 +122,14 @@ $summary['min_humidity'] = round_or_null($row['min_humidity'] ?? null); $summary['max_humidity'] = round_or_null($row['max_humidity'] ?? null); // ── 센서별 SMS 태그별 건수 + 실패 건수 ─────────────────────────────────────── +// sensor_id 로만 묶는다(보고명 sensor_name 은 시기별로 다를 수 있어 GROUP 키에서 제외 — +// 한 센서가 여러 행으로 쪼개지는 것 방지). 표시명은 아래에서 sensor_label() 로 결정. $stmt = $db->prepare(" - SELECT sensor_id, sensor_name, + SELECT sensor_id, MAX(sensor_name) AS sensor_name, " . sms_summary_select_sql() . " FROM leak_sms_log WHERE sent_at >= :start_at AND sent_at < :end_at - GROUP BY sensor_id, sensor_name + GROUP BY sensor_id "); $stmt->execute([':start_at' => $start_at, ':end_at' => $end_at]); $sms_by_sensor = []; @@ -144,8 +138,11 @@ foreach ($stmt->fetchAll() as $r) { } // ── 센서별 측정 집계(온습도) ───────────────────────────────────────────────── +// 마찬가지로 sensor_id 로만 묶고, 보고명/위치는 최신 대표값(MAX)만 폴백용으로 가져온다. $stmt = $db->prepare(" - SELECT sensor_id, sensor_name, device_location, + SELECT sensor_id, + MAX(sensor_name) AS sensor_name, + MAX(device_location) AS device_location, COUNT(*) AS metrics, AVG(temperature_c) AS avg_temp, MIN(temperature_c) AS min_temp, @@ -156,7 +153,7 @@ $stmt = $db->prepare(" MAX(measured_at) AS last_measured_at FROM leak_sensor_metric WHERE measured_at >= :start_at AND measured_at < :end_at - GROUP BY sensor_id, sensor_name, device_location + GROUP BY sensor_id "); $stmt->execute([':start_at' => $start_at, ':end_at' => $end_at]); $metric_by_sensor = []; @@ -169,16 +166,22 @@ $sensor_rows = []; foreach (array_unique(array_merge(array_keys($metric_by_sensor), array_keys($sms_by_sensor))) as $sid) { $m = $metric_by_sensor[$sid] ?? []; $s = $sms_by_sensor[$sid] ?? []; + // 표시명/위치: SENSOR_LABELS(현재 관리주체) 우선, 없으면 보고 대표값 폴백 — 대시보드·SMS 와 동일 + [$loc, $name] = sensor_label((int)$sid, $m['device_location'] ?? null, $m['sensor_name'] ?? $s['sensor_name'] ?? null); + $threshold_alerts = (int)($s['high_temp'] ?? 0) + (int)($s['low_temp'] ?? 0) + + (int)($s['high_humidity'] ?? 0) + (int)($s['low_humidity'] ?? 0); + $leak_alert = (int)($s['leak_alert'] ?? 0); $sensor_rows[] = [ 'sensor_id' => $sid, - 'sensor_name' => $m['sensor_name'] ?? $s['sensor_name'] ?? ('센서' . $sid), - 'device_location' => $m['device_location'] ?? '', + 'sensor_name' => $name, + 'device_location' => $loc, 'high_temp' => (int)($s['high_temp'] ?? 0), 'low_temp' => (int)($s['low_temp'] ?? 0), 'high_humidity' => (int)($s['high_humidity'] ?? 0), 'low_humidity' => (int)($s['low_humidity'] ?? 0), - 'threshold_alerts' => (int)($s['high_temp'] ?? 0) + (int)($s['low_temp'] ?? 0) - + (int)($s['high_humidity'] ?? 0) + (int)($s['low_humidity'] ?? 0), + 'threshold_alerts' => $threshold_alerts, + 'leak_alert' => $leak_alert, + 'leak_recovery' => (int)($s['leak_recovery'] ?? 0), 'recovery' => (int)($s['recovery'] ?? 0), 'device_offline' => (int)($s['device_offline'] ?? 0), 'device_online' => (int)($s['device_online'] ?? 0), @@ -191,10 +194,11 @@ foreach (array_unique(array_merge(array_keys($metric_by_sensor), array_keys($sms 'min_humidity' => round_or_null($m['min_humidity'] ?? null), 'max_humidity' => round_or_null($m['max_humidity'] ?? null), 'last_measured_at' => $m['last_measured_at'] ?? '', + 'alert_sort' => $threshold_alerts + $leak_alert, // 정렬용(온습도 경보 + 누수 감지) ]; } usort($sensor_rows, function ($a, $b) { - return [$b['threshold_alerts'], $a['sensor_id']] <=> [$a['threshold_alerts'], $b['sensor_id']]; + return [$b['alert_sort'], $a['sensor_id']] <=> [$a['alert_sort'], $b['sensor_id']]; }); // ── CSV 다운로드 ───────────────────────────────────────────────────────────── @@ -210,6 +214,8 @@ if (($_GET['format'] ?? '') === 'csv') { fputcsv($out, ['low_temp_alerts', $summary['low_temp']]); fputcsv($out, ['high_humidity_alerts', $summary['high_humidity']]); fputcsv($out, ['low_humidity_alerts', $summary['low_humidity']]); + fputcsv($out, ['leak_alert_sms', $summary['leak_alert']]); + fputcsv($out, ['leak_recovery_sms', $summary['leak_recovery']]); fputcsv($out, ['recovery_sms', $summary['recovery']]); fputcsv($out, ['device_offline_sms', $summary['device_offline']]); fputcsv($out, ['device_online_sms', $summary['device_online']]); @@ -225,6 +231,7 @@ if (($_GET['format'] ?? '') === 'csv') { fputcsv($out, [ 'sensor_id', 'sensor_name', 'location', 'high_temp', 'low_temp', 'high_humidity', 'low_humidity', 'threshold_alerts', + 'leak_alert', 'leak_recovery', 'recovery', 'device_offline', 'device_online', 'sms_fail', 'metrics', 'avg_temp_c', 'min_temp_c', 'max_temp_c', 'avg_humidity_percent', 'min_humidity_percent', 'max_humidity_percent', @@ -234,6 +241,7 @@ if (($_GET['format'] ?? '') === 'csv') { fputcsv($out, [ $s['sensor_id'], csv_cell($s['sensor_name']), csv_cell($s['device_location']), $s['high_temp'], $s['low_temp'], $s['high_humidity'], $s['low_humidity'], $s['threshold_alerts'], + $s['leak_alert'], $s['leak_recovery'], $s['recovery'], $s['device_offline'], $s['device_online'], $s['sms_fail'], $s['metrics'], $s['avg_temp'] ?? '', $s['min_temp'] ?? '', $s['max_temp'] ?? '', @@ -304,6 +312,7 @@ if (($_GET['format'] ?? '') === 'csv') {
임계 경보(합계)건
고온/저온 경보/건
고습/저습 경보/건
+
누수 감지/복구/건
정상복귀건
장비 오프라인/복구/건
SMS 실패건
@@ -318,7 +327,7 @@ if (($_GET['format'] ?? '') === 'csv') { - + @@ -331,6 +340,7 @@ if (($_GET['format'] ?? '') === 'csv') { + @@ -342,7 +352,7 @@ if (($_GET['format'] ?? '') === 'csv') { - +
센서위치임계 경보고온/저온고습/저습임계 경보고온/저온고습/저습누수 감지/복구 정상복귀오프라인/복구SMS 실패 측정평균 온도/습도온도 최저/최고습도 최저/최고 최근 측정 / // /
해당 월 측정·경보 내역이 없습니다.
해당 월 측정·경보 내역이 없습니다.