Update: 2026-07-12 05:40:28

This commit is contained in:
Hamza-Ayed
2026-07-12 05:40:28 +03:00
parent 1fa517acc9
commit 24fb56f08f
75 changed files with 5051 additions and 9 deletions
+135 -4
View File
@@ -182,6 +182,61 @@ function forwardLocationToPassengerSocket(
);
}
// ============================================================
// 🚌 Forward موقع الباص → سيرفر الراكب (بثّ لغرفة الخط، ASYNC + throttle)
// نفس آلية forwardLocationToPassengerSocket لكن للبثّ الجماعي
// (باص واحد → كل ركاب الخط) بدل (سائق → راكب واحد)
// ============================================================
function forwardBusLocationToRoute(
int $tripId,
int $routeId,
array $payload,
string $internalKey,
array &$busThrottle
): void {
if ($routeId <= 0) return;
$now = time();
$last = $busThrottle[$tripId] ?? null;
if ($last !== null) {
$timeDiff = $now - $last['ts'];
$dist = haversineDistance(
$last['lat'], $last['lng'],
(float)$payload['latitude'], (float)$payload['longitude']
);
if ($dist < FORWARD_MIN_METERS && $timeDiff < FORWARD_MAX_SECONDS) return;
}
$busThrottle[$tripId] = [
'ts' => $now,
'lat' => (float)$payload['latitude'],
'lng' => (float)$payload['longitude'],
];
$passengerSocketUrl = getenv('PASSENGER_SOCKET_INTERNAL_URL') ?: 'http://127.0.0.1:3031';
$http = new AsyncHttp();
$http->request(
$passengerSocketUrl,
[
'method' => 'POST',
'data' => http_build_query([
'action' => 'broadcast_bus_location',
'route_id' => $routeId,
'payload' => json_encode($payload),
]),
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'x-internal-key' => $internalKey,
'Connection' => 'close',
],
'timeout' => 3,
],
null,
fn(\Exception $e) => logMsg('⚠️ Bus forward failed: ' . $e->getMessage())
);
}
// ============================================================
// 📲 FCM (ASYNC)
// ============================================================
@@ -215,9 +270,10 @@ function sendFCM_Async(string $token, string $title, string $body, array $rideDa
// ============================================================
$connectedDrivers = [];
$active_orders_drivers = [];
$driverState = [];
$fwdThrottle = [];
$driverState = [];
$fwdThrottle = [];
$eventBuffer = []; // 🚀 Level 2: مصفوفة تجميع الأحداث لـ Redis
$busFwdThrottle = []; // 🚌 throttle بثّ موقع الباص لكل رحلة (trip_id)
// ============================================================
// 🚀 Socket.IO — بورت 2020
@@ -592,6 +648,21 @@ $io->on('workerStart', function () use ($io, $INTERNAL_KEY) {
$connection->send('Error');
}
// ── 8. 🚌 Get Bus Position (آخر موقع للباص — للتحميل الأولي) ─
// يستدعيه الباك اند (transit/trip/live.php) ليعطي الراكب آخر
// موقع معروف فوراً عند فتح الخط، قبل أول بثّ حي عبر السوكت.
} elseif ($action === 'get_bus_position') {
$tripId = (int)($post['trip_id'] ?? 0);
if ($tripId <= 0 || !$redis) {
$connection->send(json_encode(['status' => false, 'data' => null]));
return;
}
$pos = $redis->hgetall("transit:trip:$tripId:pos");
$connection->send(json_encode([
'status' => !empty($pos),
'data' => $pos ?: null,
]));
} else {
$connection->send('Unknown action');
}
@@ -604,7 +675,7 @@ $io->on('workerStart', function () use ($io, $INTERNAL_KEY) {
// B. WebSocket Events للسائقين
// ============================================================
$io->on('connection', function ($socket) use ($INTERNAL_KEY) {
global $connectedDrivers, $driverState, $fwdThrottle, $eventBuffer;
global $connectedDrivers, $driverState, $fwdThrottle, $eventBuffer, $busFwdThrottle;
$query = $socket->handshake['query'] ?? [];
$driverId = $query['driver_id'] ?? null;
@@ -826,13 +897,73 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
}
});
// ── 🚌 وضع الباص (مواصلاتي) ────────────────────────────────
// سائق الباص هو سائق عادي (JWT role=driver) لكنه في وضع الباص لا
// يدخل حوض الرحلات (geo:drivers:*). يبعث update_bus_location فقط.
// نخزّن آخر موقع في Redis سيرفر الموقع + نبثّه لغرفة الخط.
$socket->on('update_bus_location', function ($data)
use ($driverId, $INTERNAL_KEY, &$busFwdThrottle)
{
$data = (array) $data;
$tripId = (int)($data['trip_id'] ?? 0);
$lat = isset($data['lat']) ? (float)$data['lat'] : null;
$lng = isset($data['lng']) ? (float)$data['lng'] : null;
$heading = (float)($data['heading'] ?? 0);
$speed = (float)($data['speed'] ?? 0);
$stopSeq = isset($data['current_stop_seq']) ? (int)$data['current_stop_seq'] : null;
if (!$tripId || $lat === null || $lng === null) return;
$redis = getRedis();
if (!$redis) return;
// ── تحقق الملكية: هذه الرحلة فعلاً مسندة لهذا السائق؟ ────────
// الكاش يُكتب من transit/trip/start.php عند بدء الرحلة (transitSetTripOwner)
// ويُحذف عند إنهائها. أي سائق آخر (حتى لو خمّن trip_id صحيح) يُرفض هنا،
// ولا نثق بـ route_id القادم من العميل — نأخذه دائماً من الكاش الموثوق.
$owner = $redis->hgetall("transit:trip:$tripId:owner");
if (empty($owner) || (string)($owner['driver_id'] ?? '') !== (string)$driverId) {
logMsg("🚫 update_bus_location rejected: driver #$driverId is not the owner of trip #$tripId");
return;
}
$routeId = (int)($owner['route_id'] ?? 0);
if ($routeId <= 0) return;
// 1. آخر موقع في Redis سيرفر الموقع (بدون بادئة — يقرؤه الباك اند عبر $redisLocation)
$posKey = "transit:trip:$tripId:pos";
$redis->hmset($posKey, [
'lat' => $lat,
'lng' => $lng,
'heading' => $heading,
'speed' => $speed,
'driver_id' => $driverId,
'ts' => time(),
]);
$redis->expire($posKey, 86400);
// 2. بثّ الموقع لكل ركاب الخط عبر سيرفر الراكب (throttled)
forwardBusLocationToRoute($tripId, $routeId, [
'trip_id' => $tripId,
'route_id' => $routeId,
'latitude' => $lat,
'longitude' => $lng,
'heading' => $heading,
'speed' => $speed,
'current_stop_seq' => $stopSeq,
'driver_id' => $driverId,
], $INTERNAL_KEY, $busFwdThrottle);
});
$socket->on('disconnect', function () use ($driverId) {
global $connectedDrivers, $driverState, $fwdThrottle;
unset($connectedDrivers[$driverId]);
unset($driverState[$driverId]);
unset($fwdThrottle[$driverId]);
// ملاحظة: $busFwdThrottle مُفهرس بـ trip_id (لا driver_id) — يُستبدَل
// تلقائياً في الرحلة التالية، فلا حاجة لحذفه هنا.
logMsg("❌ Driver Disconnected: #$driverId");
});
});