데이터 사용량 계산을 통신사 과금 단위에 맞춤
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저장하고, 웹 화면과 API에서 최신 상태와 명령 실행 결과를 제공하는 내부 차량 관리 서비스입니다.
|
Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저장하고, 웹 화면과 API에서 최신 상태와 명령 실행 결과를 제공하는 내부 차량 관리 서비스입니다.
|
||||||
|
|
||||||
모뎀에서 제공되는 값의 한계가 있으므로 도어 잠김, 경계, 시동, 공조 상태는 여러 필드를 조합해 해석합니다. 통신사 사용량은 DB 수집값과 T world 기준 보정값을 함께 사용합니다.
|
모뎀에서 제공되는 값의 한계가 있으므로 도어 잠김, 경계, 시동, 공조 상태는 여러 필드를 조합해 해석합니다. 통신사 사용량은 TCP payload 수집값, 통신사 과금 단위 추정값, T world 기준 보정값을 함께 사용합니다.
|
||||||
|
|
||||||
## 주요 기능
|
## 주요 기능
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
|
|||||||
- monitor 화면 WakeLock, 언어, 테마 토글
|
- monitor 화면 WakeLock, 언어, 테마 토글
|
||||||
- monitor 좌측 상단 블로그 복귀 링크
|
- monitor 좌측 상단 블로그 복귀 링크
|
||||||
- 모바일 및 좁은 카드 폭에 맞춘 사용량 2줄 배치, 로그 가로 스크롤, 시간 문구 축약
|
- 모바일 및 좁은 카드 폭에 맞춘 사용량 2줄 배치, 로그 가로 스크롤, 시간 문구 축약
|
||||||
- 사용량/요금 표시와 보정 metadata
|
- 사용량/요금 표시와 통신사 과금 단위 보정 metadata
|
||||||
- 4.95초 UI timeout과 60초 사용량 정산 분리
|
- 4.95초 UI timeout과 60초 사용량 정산 분리
|
||||||
- TCP 실패 reason과 마지막 수신 지연 표시
|
- TCP 실패 reason과 마지막 수신 지연 표시
|
||||||
- 수집 공백을 반영한 차량 상태 경과 시간 표시
|
- 수집 공백을 반영한 차량 상태 경과 시간 표시
|
||||||
@@ -62,7 +62,7 @@ Car는 차량 모뎀/게이트웨이에서 받은 상태를 수집해 DB에 저
|
|||||||
## 데이터/저장소
|
## 데이터/저장소
|
||||||
|
|
||||||
- Car DB tables: 차량 상태, 로그, 차트용 기록, TCP 요청 큐
|
- Car DB tables: 차량 상태, 로그, 차트용 기록, TCP 요청 큐
|
||||||
- 사용량 수집값과 월별 보정 기준
|
- 사용량 수집값, 통신사 과금 단위 추정값, 월별 보정 기준
|
||||||
- `/home/seo/secret/car.php`: TCP, DB, token, 허용 IP 설정
|
- `/home/seo/secret/car.php`: TCP, DB, token, 허용 IP 설정
|
||||||
|
|
||||||
## 처리 흐름
|
## 처리 흐름
|
||||||
@@ -126,8 +126,9 @@ Worker 교체나 예외 종료로 `processing`, `ui_timeout_pending` 상태가
|
|||||||
|
|
||||||
- 차량 TCP 실패 reason과 마지막 수신 지연을 확인합니다.
|
- 차량 TCP 실패 reason과 마지막 수신 지연을 확인합니다.
|
||||||
- 명령별 rate limit과 감사 로그를 유지합니다.
|
- 명령별 rate limit과 감사 로그를 유지합니다.
|
||||||
- 통신사 기준 데이터 사용량 보정값을 주기적으로 확인합니다. 2026-06-29 05:13 T world 실측 109.9MB(112,507.5KB) 기준 2026-06 보정값은 통신사 누적 115,207,680 bytes, 로컬 보정 누적 57,853,769 bytes, 배율 1.991360입니다.
|
- 통신사 기준 데이터 사용량 보정값을 주기적으로 확인합니다. 2026-06-30 15:15 T world 실측 114.5MB(117,250.5KB) 기준 2026-06 보정값은 통신사 누적 120,064,512 bytes, 통신사 과금 단위 추정 누적 129,148,416 bytes, 배율 0.929663입니다.
|
||||||
- 해당 기준 시각의 로컬 계측 범위는 2026-06-01 00:00:00부터 2026-06-29 05:13:00 직전까지이며, 마지막 로컬 사용량 기록은 2026-06-29 05:12:49입니다.
|
- 해당 기준 시각의 로컬 계측 범위는 2026-06-01 00:00:00부터 2026-06-30 15:15:00 직전까지이며, 마지막 로컬 사용량 기록은 2026-06-30 15:14:59입니다.
|
||||||
|
- 데이터 사용량은 TCP payload 합계도 보존하지만 화면의 현재 사용량, 잔여 사용량, 예상 사용량, 예상 요금은 0.5KB 과금 단위로 올림 처리한 통신사형 누적값을 우선 사용합니다. 순수 연결 실패와 마지막 성공 이후 60초를 넘긴 수신 공백은 기존처럼 제외합니다.
|
||||||
- 데이터 사용량 집계는 성공 응답 평균을 SQL로 먼저 구하고, 월간/일간 행은 PHP에서 한 줄씩 스트리밍 처리합니다. 기존 보정 조건은 유지하면서 `fetchAll()` 메모리 사용을 제거해 PHP-FPM `memory_limit=128M`에서도 동작하도록 합니다.
|
- 데이터 사용량 집계는 성공 응답 평균을 SQL로 먼저 구하고, 월간/일간 행은 PHP에서 한 줄씩 스트리밍 처리합니다. 기존 보정 조건은 유지하면서 `fetchAll()` 메모리 사용을 제거해 PHP-FPM `memory_limit=128M`에서도 동작하도록 합니다.
|
||||||
- 4.95초 timeout 직후에는 사용량 기록이 즉시 생기지 않을 수 있으며, 최대 60초 뒤 늦은 정산 행이 추가될 수 있습니다.
|
- 4.95초 timeout 직후에는 사용량 기록이 즉시 생기지 않을 수 있으며, 최대 60초 뒤 늦은 정산 행이 추가될 수 있습니다.
|
||||||
- `systemctl status car-tcp-worker.service`와 `journalctl -u car-tcp-worker.service`로 worker 상태를 확인합니다.
|
- `systemctl status car-tcp-worker.service`와 `journalctl -u car-tcp-worker.service`로 worker 상태를 확인합니다.
|
||||||
|
|||||||
+35
-2
@@ -37,6 +37,7 @@ define('ALLOWED_IPS', array_values(array_filter(
|
|||||||
|
|
||||||
const ALLOWED_CMD = ['se', 'ef', 'en', 'hn', 'hf', 'dl', 'du', 'tu'];
|
const ALLOWED_CMD = ['se', 'ef', 'en', 'hn', 'hf', 'dl', 'du', 'tu'];
|
||||||
const CONTROL_CMD = ['ef', 'en', 'hn', 'hf', 'dl', 'du', 'tu'];
|
const CONTROL_CMD = ['ef', 'en', 'hn', 'hf', 'dl', 'du', 'tu'];
|
||||||
|
const CARRIER_BILLING_UNIT_BYTES = 512;
|
||||||
|
|
||||||
const RAW_FULL_REGEX = '/(?<modem>\d{11})\/R:(?<r>[a-z])\/E:(?<E0>[io]{5}\d{3}[io])\/D:(?<D0>[oi]{7})\/L:(?<L0>o{5})\/F:(?<F0>[ots][oi]\d{4}[oi]{4})\/S:(?<S0>[^\/]+)/';
|
const RAW_FULL_REGEX = '/(?<modem>\d{11})\/R:(?<r>[a-z])\/E:(?<E0>[io]{5}\d{3}[io])\/D:(?<D0>[oi]{7})\/L:(?<L0>o{5})\/F:(?<F0>[ots][oi]\d{4}[oi]{4})\/S:(?<S0>[^\/]+)/';
|
||||||
|
|
||||||
@@ -113,12 +114,14 @@ function db_insert_data_usage(
|
|||||||
?string $requestId = null
|
?string $requestId = null
|
||||||
): void {
|
): void {
|
||||||
try {
|
try {
|
||||||
|
db_ensure_data_usage_schema($pdo);
|
||||||
|
$carrierEstimatedBytes = carrier_billing_bytes($sentBytes + $receivedBytes);
|
||||||
$stmt = $pdo->prepare("INSERT INTO car_data_usage (
|
$stmt = $pdo->prepare("INSERT INTO car_data_usage (
|
||||||
id, ts, source, cmd, sent_bytes, received_bytes, total_bytes,
|
id, ts, source, cmd, sent_bytes, received_bytes, total_bytes,
|
||||||
tcp_ok, tcp_error, request_id
|
carrier_estimated_bytes, tcp_ok, tcp_error, request_id
|
||||||
) VALUES (
|
) VALUES (
|
||||||
'car', :ts, :source, :cmd, :sent_bytes, :received_bytes, :total_bytes,
|
'car', :ts, :source, :cmd, :sent_bytes, :received_bytes, :total_bytes,
|
||||||
:tcp_ok, :tcp_error, :request_id
|
:carrier_estimated_bytes, :tcp_ok, :tcp_error, :request_id
|
||||||
)");
|
)");
|
||||||
|
|
||||||
$stmt->execute([
|
$stmt->execute([
|
||||||
@@ -128,6 +131,7 @@ function db_insert_data_usage(
|
|||||||
':sent_bytes' => max(0, $sentBytes),
|
':sent_bytes' => max(0, $sentBytes),
|
||||||
':received_bytes' => max(0, $receivedBytes),
|
':received_bytes' => max(0, $receivedBytes),
|
||||||
':total_bytes' => max(0, $sentBytes + $receivedBytes),
|
':total_bytes' => max(0, $sentBytes + $receivedBytes),
|
||||||
|
':carrier_estimated_bytes' => $carrierEstimatedBytes,
|
||||||
':tcp_ok' => $tcpOk ? 1 : 0,
|
':tcp_ok' => $tcpOk ? 1 : 0,
|
||||||
':tcp_error' => $tcpError !== '' ? substr($tcpError, 0, 100) : null,
|
':tcp_error' => $tcpError !== '' ? substr($tcpError, 0, 100) : null,
|
||||||
':request_id' => $requestId,
|
':request_id' => $requestId,
|
||||||
@@ -137,6 +141,35 @@ function db_insert_data_usage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function carrier_billing_bytes(int $payloadBytes): int
|
||||||
|
{
|
||||||
|
$payloadBytes = max(0, $payloadBytes);
|
||||||
|
if ($payloadBytes <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(ceil($payloadBytes / CARRIER_BILLING_UNIT_BYTES) * CARRIER_BILLING_UNIT_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_ensure_data_usage_schema(PDO $pdo): void
|
||||||
|
{
|
||||||
|
static $done = false;
|
||||||
|
if ($done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM car_data_usage LIKE 'carrier_estimated_bytes'");
|
||||||
|
if (!$stmt || !$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE car_data_usage ADD COLUMN carrier_estimated_bytes INT UNSIGNED NOT NULL DEFAULT 0 AFTER total_bytes");
|
||||||
|
}
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
// 스키마 보강 실패가 차량 제어/조회 자체를 막으면 안 된다.
|
||||||
|
}
|
||||||
|
|
||||||
|
$done = true;
|
||||||
|
}
|
||||||
|
|
||||||
function record_tcp_usage(
|
function record_tcp_usage(
|
||||||
string $source,
|
string $source,
|
||||||
string $cmd,
|
string $cmd,
|
||||||
|
|||||||
+52
-12
@@ -25,9 +25,9 @@ const DATA_MONTHLY_ADDON_FEE_KRW = 2200;
|
|||||||
const RECEIVE_GAP_LIMIT_SEC = 60;
|
const RECEIVE_GAP_LIMIT_SEC = 60;
|
||||||
|
|
||||||
// SKT T world "잔여기본통화 정보" 기준 현재월 누적값.
|
// SKT T world "잔여기본통화 정보" 기준 현재월 누적값.
|
||||||
// carrier_total_bytes / meter_adjusted_bytes 로 보정 배율을 역산해 로컬 송수신 계측값에 적용한다.
|
// carrier_total_bytes / meter_adjusted_bytes 로 보정 배율을 역산해 통신사 과금 단위 계측값에 적용한다.
|
||||||
const DATA_CURRENT_BILLING_USAGE_BYTES = [
|
const DATA_CURRENT_BILLING_USAGE_BYTES = [
|
||||||
'2026-06' => ['total_bytes' => 115207680, 'meter_adjusted_bytes' => 57853769, 'included_bytes' => 104857600, 'coupon_registered_at' => null],
|
'2026-06' => ['total_bytes' => 120064512, 'meter_adjusted_bytes' => 129148416, 'included_bytes' => 104857600, 'coupon_registered_at' => null],
|
||||||
];
|
];
|
||||||
|
|
||||||
function car_db(): PDO
|
function car_db(): PDO
|
||||||
@@ -44,10 +44,26 @@ function car_db(): PDO
|
|||||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
PDO::ATTR_EMULATE_PREPARES => false,
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
]);
|
]);
|
||||||
|
db_ensure_data_usage_schema($pdo);
|
||||||
|
|
||||||
return $pdo;
|
return $pdo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function db_ensure_data_usage_schema(PDO $pdo): void
|
||||||
|
{
|
||||||
|
static $done = false;
|
||||||
|
if ($done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt = $pdo->query("SHOW COLUMNS FROM car_data_usage LIKE 'carrier_estimated_bytes'");
|
||||||
|
if (!$stmt || !$stmt->fetch()) {
|
||||||
|
$pdo->exec("ALTER TABLE car_data_usage ADD COLUMN carrier_estimated_bytes INT UNSIGNED NOT NULL DEFAULT 0 AFTER total_bytes");
|
||||||
|
}
|
||||||
|
|
||||||
|
$done = true;
|
||||||
|
}
|
||||||
|
|
||||||
function seconds_from_ts(?string $ts): ?int
|
function seconds_from_ts(?string $ts): ?int
|
||||||
{
|
{
|
||||||
if (!$ts) {
|
if (!$ts) {
|
||||||
@@ -213,8 +229,9 @@ function monthly_data_usage(PDO $pdo): array
|
|||||||
$receivedBytes = $usage['received_bytes'];
|
$receivedBytes = $usage['received_bytes'];
|
||||||
$totalBytes = $usage['total_bytes'];
|
$totalBytes = $usage['total_bytes'];
|
||||||
$adjustedTotalBytes = $usage['adjusted_total_bytes'];
|
$adjustedTotalBytes = $usage['adjusted_total_bytes'];
|
||||||
$remainingBytes = max(DATA_MONTHLY_INCLUDED_BYTES - $adjustedTotalBytes, 0);
|
$carrierAdjustedTotalBytes = $usage['carrier_adjusted_total_bytes'];
|
||||||
$overBytes = max($adjustedTotalBytes - DATA_MONTHLY_INCLUDED_BYTES, 0);
|
$remainingBytes = max(DATA_MONTHLY_INCLUDED_BYTES - $carrierAdjustedTotalBytes, 0);
|
||||||
|
$overBytes = max($carrierAdjustedTotalBytes - DATA_MONTHLY_INCLUDED_BYTES, 0);
|
||||||
$overUnits = $overBytes > 0 ? (int)ceil($overBytes / DATA_BILLING_UNIT_BYTES) : 0;
|
$overUnits = $overBytes > 0 ? (int)ceil($overBytes / DATA_BILLING_UNIT_BYTES) : 0;
|
||||||
$overFeeKrw = $overUnits * DATA_BILLING_UNIT_KRW;
|
$overFeeKrw = $overUnits * DATA_BILLING_UNIT_KRW;
|
||||||
|
|
||||||
@@ -229,9 +246,9 @@ function monthly_data_usage(PDO $pdo): array
|
|||||||
$measuredSeconds = max(1, $now->getTimestamp() - $projectionStart);
|
$measuredSeconds = max(1, $now->getTimestamp() - $projectionStart);
|
||||||
$currentBilling = current_billing_usage($monthStart->format('Y-m'));
|
$currentBilling = current_billing_usage($monthStart->format('Y-m'));
|
||||||
$billingScale = billing_meter_scale($currentBilling);
|
$billingScale = billing_meter_scale($currentBilling);
|
||||||
$billingCurrentBytes = (int)round($adjustedTotalBytes * $billingScale);
|
$billingCurrentBytes = (int)round($carrierAdjustedTotalBytes * $billingScale);
|
||||||
$projectedBytes = (int)round($adjustedTotalBytes / $measuredSeconds * $monthSeconds);
|
$projectedBytes = (int)round($carrierAdjustedTotalBytes / $measuredSeconds * $monthSeconds);
|
||||||
$billingProjectedBytes = $adjustedTotalBytes > 0
|
$billingProjectedBytes = $carrierAdjustedTotalBytes > 0
|
||||||
? max((int)round($projectedBytes * $billingScale), $billingCurrentBytes)
|
? max((int)round($projectedBytes * $billingScale), $billingCurrentBytes)
|
||||||
: 0;
|
: 0;
|
||||||
$calibration = billing_calibration($currentBilling, $billingScale);
|
$calibration = billing_calibration($currentBilling, $billingScale);
|
||||||
@@ -249,7 +266,7 @@ function monthly_data_usage(PDO $pdo): array
|
|||||||
$billingProjectedOverFeeKrw = $billingProjectedOverUnits * DATA_BILLING_UNIT_KRW;
|
$billingProjectedOverFeeKrw = $billingProjectedOverUnits * DATA_BILLING_UNIT_KRW;
|
||||||
$fixedFeeKrw = DATA_MONTHLY_BASE_FEE_KRW + DATA_MONTHLY_ADDON_FEE_KRW;
|
$fixedFeeKrw = DATA_MONTHLY_BASE_FEE_KRW + DATA_MONTHLY_ADDON_FEE_KRW;
|
||||||
$dailyRecommendedBytes = (int)floor(DATA_MONTHLY_INCLUDED_BYTES / max(1, $daysInMonth));
|
$dailyRecommendedBytes = (int)floor(DATA_MONTHLY_INCLUDED_BYTES / max(1, $daysInMonth));
|
||||||
$estimatedTodayBytes = (int)round($todayUsage['adjusted_total_bytes'] * $billingScale);
|
$estimatedTodayBytes = (int)round($todayUsage['carrier_adjusted_total_bytes'] * $billingScale);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'month' => $monthStart->format('Y-m'),
|
'month' => $monthStart->format('Y-m'),
|
||||||
@@ -265,15 +282,18 @@ function monthly_data_usage(PDO $pdo): array
|
|||||||
'received_bytes' => $receivedBytes,
|
'received_bytes' => $receivedBytes,
|
||||||
'total_bytes' => $totalBytes,
|
'total_bytes' => $totalBytes,
|
||||||
'adjusted_total_bytes' => $adjustedTotalBytes,
|
'adjusted_total_bytes' => $adjustedTotalBytes,
|
||||||
|
'carrier_adjusted_total_bytes' => $carrierAdjustedTotalBytes,
|
||||||
|
'carrier_adjusted_failure_bytes' => $usage['carrier_adjusted_failure_bytes'],
|
||||||
'billing_sent_bytes' => (int)round($sentBytes * $billingScale),
|
'billing_sent_bytes' => (int)round($sentBytes * $billingScale),
|
||||||
'billing_received_bytes' => (int)round($receivedBytes * $billingScale),
|
'billing_received_bytes' => (int)round($receivedBytes * $billingScale),
|
||||||
'billing_adjusted_failure_bytes' => (int)round($usage['adjusted_failure_bytes'] * $billingScale),
|
'billing_adjusted_failure_bytes' => (int)round($usage['carrier_adjusted_failure_bytes'] * $billingScale),
|
||||||
'connected_failure_count' => $usage['connected_failure_count'],
|
'connected_failure_count' => $usage['connected_failure_count'],
|
||||||
'connect_failure_count' => $usage['connect_failure_count'],
|
'connect_failure_count' => $usage['connect_failure_count'],
|
||||||
'receive_gap_excluded_count' => $usage['receive_gap_excluded_count'],
|
'receive_gap_excluded_count' => $usage['receive_gap_excluded_count'],
|
||||||
'adjusted_failure_bytes' => $usage['adjusted_failure_bytes'],
|
'adjusted_failure_bytes' => $usage['adjusted_failure_bytes'],
|
||||||
'today_total_bytes' => $todayUsage['total_bytes'],
|
'today_total_bytes' => $todayUsage['total_bytes'],
|
||||||
'today_adjusted_total_bytes' => $todayUsage['adjusted_total_bytes'],
|
'today_adjusted_total_bytes' => $todayUsage['adjusted_total_bytes'],
|
||||||
|
'today_carrier_adjusted_total_bytes' => $todayUsage['carrier_adjusted_total_bytes'],
|
||||||
'today_sample_count' => $todayUsage['sample_count'],
|
'today_sample_count' => $todayUsage['sample_count'],
|
||||||
'today_connected_failure_count' => $todayUsage['connected_failure_count'],
|
'today_connected_failure_count' => $todayUsage['connected_failure_count'],
|
||||||
'today_connect_failure_count' => $todayUsage['connect_failure_count'],
|
'today_connect_failure_count' => $todayUsage['connect_failure_count'],
|
||||||
@@ -365,6 +385,16 @@ function usage_success_averages(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function carrier_billing_bytes(int $payloadBytes): int
|
||||||
|
{
|
||||||
|
$payloadBytes = max(0, $payloadBytes);
|
||||||
|
if ($payloadBytes <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)(ceil($payloadBytes / DATA_BILLING_UNIT_BYTES) * DATA_BILLING_UNIT_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInterface $end): array
|
function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInterface $end): array
|
||||||
{
|
{
|
||||||
$averages = usage_success_averages($pdo, $start, $end);
|
$averages = usage_success_averages($pdo, $start, $end);
|
||||||
@@ -379,6 +409,8 @@ function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
|
|
||||||
$adjustedTotalBytes = 0;
|
$adjustedTotalBytes = 0;
|
||||||
$adjustedFailureBytes = 0;
|
$adjustedFailureBytes = 0;
|
||||||
|
$carrierAdjustedTotalBytes = 0;
|
||||||
|
$carrierAdjustedFailureBytes = 0;
|
||||||
$connectedFailureCount = 0;
|
$connectedFailureCount = 0;
|
||||||
$connectFailureCount = 0;
|
$connectFailureCount = 0;
|
||||||
$receiveGapExcludedCount = 0;
|
$receiveGapExcludedCount = 0;
|
||||||
@@ -391,7 +423,7 @@ function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$stmt = $pdo->prepare(""
|
$stmt = $pdo->prepare(""
|
||||||
. "SELECT ts, cmd, sent_bytes, received_bytes, total_bytes, tcp_ok "
|
. "SELECT ts, cmd, sent_bytes, received_bytes, total_bytes, carrier_estimated_bytes, tcp_ok "
|
||||||
. "FROM car_data_usage "
|
. "FROM car_data_usage "
|
||||||
. "WHERE id='car' AND ts >= :start_ts AND ts < :end_ts "
|
. "WHERE id='car' AND ts >= :start_ts AND ts < :end_ts "
|
||||||
. "ORDER BY ts ASC"
|
. "ORDER BY ts ASC"
|
||||||
@@ -423,6 +455,9 @@ function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
|
|
||||||
if ($tcpOk) {
|
if ($tcpOk) {
|
||||||
$adjustedTotalBytes += $total;
|
$adjustedTotalBytes += $total;
|
||||||
|
$carrierAdjustedTotalBytes += (int)($row['carrier_estimated_bytes'] ?? 0) > 0
|
||||||
|
? (int)$row['carrier_estimated_bytes']
|
||||||
|
: carrier_billing_bytes($total);
|
||||||
if ($rowTs !== false) {
|
if ($rowTs !== false) {
|
||||||
$lastSuccessfulTs = $rowTs;
|
$lastSuccessfulTs = $rowTs;
|
||||||
}
|
}
|
||||||
@@ -454,6 +489,9 @@ function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
|
|
||||||
$adjustedTotalBytes += $estimatedBytes;
|
$adjustedTotalBytes += $estimatedBytes;
|
||||||
$adjustedFailureBytes += $estimatedBytes;
|
$adjustedFailureBytes += $estimatedBytes;
|
||||||
|
$carrierEstimatedBytes = carrier_billing_bytes($estimatedBytes);
|
||||||
|
$carrierAdjustedTotalBytes += $carrierEstimatedBytes;
|
||||||
|
$carrierAdjustedFailureBytes += $carrierEstimatedBytes;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if ($stmt instanceof PDOStatement) {
|
if ($stmt instanceof PDOStatement) {
|
||||||
@@ -468,6 +506,8 @@ function calibrated_usage_range(PDO $pdo, DateTimeInterface $start, DateTimeInte
|
|||||||
'total_bytes' => $totalBytes,
|
'total_bytes' => $totalBytes,
|
||||||
'adjusted_total_bytes' => $adjustedTotalBytes,
|
'adjusted_total_bytes' => $adjustedTotalBytes,
|
||||||
'adjusted_failure_bytes' => $adjustedFailureBytes,
|
'adjusted_failure_bytes' => $adjustedFailureBytes,
|
||||||
|
'carrier_adjusted_total_bytes' => $carrierAdjustedTotalBytes,
|
||||||
|
'carrier_adjusted_failure_bytes' => $carrierAdjustedFailureBytes,
|
||||||
'connected_failure_count' => $connectedFailureCount,
|
'connected_failure_count' => $connectedFailureCount,
|
||||||
'connect_failure_count' => $connectFailureCount,
|
'connect_failure_count' => $connectFailureCount,
|
||||||
'receive_gap_excluded_count' => $receiveGapExcludedCount,
|
'receive_gap_excluded_count' => $receiveGapExcludedCount,
|
||||||
@@ -519,8 +559,8 @@ function billing_calibration(?array $currentBilling, float $billingScale): array
|
|||||||
return [
|
return [
|
||||||
'mode' => $source,
|
'mode' => $source,
|
||||||
'note' => $currentBilling
|
'note' => $currentBilling
|
||||||
? 'Carrier portal current usage is used to reverse-calculate a billing scale for metered local sent/received bytes. Metered local usage remains available separately.'
|
? 'Carrier portal current usage is used to reverse-calculate a billing scale for 0.5KB rounded TCP session usage. Payload metered usage remains available separately.'
|
||||||
: 'Metered usage includes successful requests and connected timeouts within 60 seconds of the last successful receive. Pure connect failures and longer receive gaps are excluded.',
|
: 'Carrier-like usage rounds each billable TCP session to the 0.5KB billing unit. Pure connect failures and longer receive gaps are excluded.',
|
||||||
'current_billing' => $currentBilling,
|
'current_billing' => $currentBilling,
|
||||||
'billing_meter_scale' => round($billingScale, 6),
|
'billing_meter_scale' => round($billingScale, 6),
|
||||||
'projected_total_bytes' => 0,
|
'projected_total_bytes' => 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user