190 lines
5.0 KiB
PHP
190 lines
5.0 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require __DIR__ . '/common.php';
|
|
|
|
$requestId = substr(bin2hex(random_bytes(16)), 0, 32);
|
|
$requestMethod = $_SERVER['REQUEST_METHOD'] ?? '';
|
|
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
|
$clientIp = get_client_ip();
|
|
$tsStart = microtime(true);
|
|
|
|
if (!in_array($clientIp, ALLOWED_IPS, true)) {
|
|
json_exit([
|
|
'error' => 'FORBIDDEN_IP',
|
|
'request_id' => $requestId,
|
|
'client_ip' => $clientIp
|
|
], 403);
|
|
}
|
|
|
|
$params = ($requestMethod === 'POST') ? $_POST : $_GET;
|
|
$token = $params['token'] ?? '';
|
|
|
|
if (!hash_equals(AUTH_TOKEN, $token)) {
|
|
json_exit([
|
|
'error' => 'FORBIDDEN_TOKEN',
|
|
'request_id' => $requestId
|
|
], 403);
|
|
}
|
|
|
|
$pdo = db();
|
|
|
|
if (($params['log'] ?? '') == '1') {
|
|
$limit = isset($params['limit']) ? (int)$params['limit'] : 50;
|
|
$logs = db_logs($pdo, $limit);
|
|
|
|
json_exit([
|
|
'request_id' => $requestId,
|
|
'limit' => $limit,
|
|
'count' => count($logs),
|
|
'logs' => $logs
|
|
]);
|
|
}
|
|
|
|
$cmdRequested = $params['cmd'] ?? 'se';
|
|
$cmd = normalize_cmd($cmdRequested);
|
|
|
|
if ($cmd === 'se') {
|
|
$latest = db_latest($pdo);
|
|
|
|
if (!$latest) {
|
|
json_exit([
|
|
'request_id' => $requestId,
|
|
'error' => 'no_latest_data'
|
|
], 500);
|
|
}
|
|
|
|
$ageSeconds = max(0, time() - strtotime((string)$latest['ts']));
|
|
$latestUsage = db_latest_usage($pdo);
|
|
$staleReason = null;
|
|
if ($ageSeconds > 30 && $latestUsage && (int)$latestUsage['tcp_ok'] === 0) {
|
|
$staleReason = (string)($latestUsage['tcp_error'] ?? 'tcp_failed');
|
|
}
|
|
|
|
json_exit([
|
|
'request_id' => $requestId,
|
|
'cmd_requested' => 'se',
|
|
'cmd' => $latest['cmd'],
|
|
'ts' => $latest['ts'],
|
|
'meta' => [
|
|
'age_seconds' => $ageSeconds,
|
|
'stale' => $ageSeconds > 30,
|
|
'stale_reason' => $staleReason,
|
|
'latest_tcp' => $latestUsage,
|
|
],
|
|
|
|
'raw_full' => $latest['raw_full'],
|
|
'raw_trim' => $latest['raw_trim'],
|
|
|
|
'data' => [
|
|
'boundary' => (int)$latest['boundary'],
|
|
'engine' => (int)$latest['engine'],
|
|
'driving' => (int)$latest['driving'],
|
|
'battery_voltage' => (float)$latest['battery_voltage'],
|
|
'door_fl' => (int)$latest['door_fl'],
|
|
'door_fr' => (int)$latest['door_fr'],
|
|
'door_rl' => (int)$latest['door_rl'],
|
|
'door_rr' => (int)$latest['door_rr'],
|
|
'door_trunk' => (int)$latest['door_trunk'],
|
|
'remote_start_preparing' => (int)$latest['remote_start_preparing'],
|
|
'remote_start_running' => (int)$latest['remote_start_running'],
|
|
'remote_start_remaining' => $latest['remote_start_remaining'],
|
|
'hazard' => (int)$latest['hazard'],
|
|
]
|
|
]);
|
|
}
|
|
|
|
if (!in_array($cmd, CONTROL_CMD, true)) {
|
|
json_exit([
|
|
'error' => 'INVALID_CMD',
|
|
'request_id' => $requestId,
|
|
'cmd' => $cmd
|
|
], 400);
|
|
}
|
|
|
|
if ($requestMethod !== 'POST') {
|
|
json_exit([
|
|
'error' => 'METHOD_NOT_ALLOWED_USE_POST',
|
|
'request_id' => $requestId,
|
|
'cmd' => $cmd
|
|
], 405);
|
|
}
|
|
|
|
$tcpMs = 0;
|
|
$connectMs = 0;
|
|
$readMs = 0;
|
|
$tcpError = '';
|
|
$trimError = '';
|
|
$sentBytes = 0;
|
|
$receivedBytes = 0;
|
|
|
|
$rawFull = tcp_request(
|
|
$cmd,
|
|
$tcpMs,
|
|
$connectMs,
|
|
$readMs,
|
|
$tcpError,
|
|
'api_control',
|
|
$requestId,
|
|
$sentBytes,
|
|
$receivedBytes
|
|
);
|
|
$execMs = (int)round((microtime(true) - $tsStart) * 1000);
|
|
|
|
if ($rawFull === '') {
|
|
json_exit([
|
|
'request_id' => $requestId,
|
|
'cmd_requested' => $cmdRequested,
|
|
'cmd' => $cmd,
|
|
'ts' => date('Y-m-d H:i:s'),
|
|
'exec_ms' => $execMs,
|
|
'client_ip' => $clientIp,
|
|
'ua' => $userAgent,
|
|
'tcp_ok' => 0,
|
|
'tcp_ms' => $tcpMs,
|
|
'connect_ms' => $connectMs,
|
|
'read_ms' => $readMs,
|
|
'sent_bytes' => $sentBytes,
|
|
'received_bytes' => $receivedBytes,
|
|
'total_bytes' => $sentBytes + $receivedBytes,
|
|
'tcp_error' => ($tcpError !== '' ? $tcpError : 'timeout_or_empty'),
|
|
'error' => 'CONTROL_CMD_SEND_FAILED'
|
|
], 502);
|
|
}
|
|
|
|
$rawTrim = make_trim($rawFull, $trimError);
|
|
$data = ($rawTrim !== '') ? parse_trim($rawTrim) : null;
|
|
|
|
if ($rawTrim !== '' && $data !== null && is_valid_status_data($data)) {
|
|
db_insert_status(
|
|
$pdo,
|
|
$cmd,
|
|
$rawFull,
|
|
$rawTrim,
|
|
$data
|
|
);
|
|
}
|
|
|
|
json_exit([
|
|
'request_id' => $requestId,
|
|
'cmd_requested' => $cmdRequested,
|
|
'cmd' => $cmd,
|
|
'ts' => date('Y-m-d H:i:s'),
|
|
'exec_ms' => $execMs,
|
|
'client_ip' => $clientIp,
|
|
'ua' => $userAgent,
|
|
'tcp_ok' => 1,
|
|
'tcp_ms' => $tcpMs,
|
|
'connect_ms' => $connectMs,
|
|
'read_ms' => $readMs,
|
|
'sent_bytes' => $sentBytes,
|
|
'received_bytes' => $receivedBytes,
|
|
'total_bytes' => $sentBytes + $receivedBytes,
|
|
'tcp_error' => $tcpError,
|
|
'accepted' => true,
|
|
'ack_only' => ($rawTrim === ''),
|
|
'raw_full' => $rawFull,
|
|
'raw_trim' => $rawTrim,
|
|
'data' => $data
|
|
]);
|