Files
AutoBooking/autobooking-simulator.php

679 lines
30 KiB
PHP

<?php
/**
* Plugin Name: AutoBooking Simulator
* Description: Entorno de simulación — 12 conductores, 20 usuarios, 2 clientes institucionales, rutas en Bogotá con desvío de ruta
* Version: 1.0.0
* Author: AutoBooking
*/
defined('ABSPATH') || exit;
define('AB_SIM_CRON', 'ab_sim_tick');
define('AB_SIM_OPT', 'ab_sim_state');
define('AB_SIM_MARKER', 'ab_sim_user');
/* ══════════════════════════════════════════════════════════════════════
* RUTAS — Miami / Hialeah, Florida
* Waypoints cada ~500-700m. Ruta C tiene desvío para probar alarmas.
* ══════════════════════════════════════════════════════════════════════ */
function ab_sim_routes() {
return [
'A' => [
'name' => 'Hialeah → Brickell (S por NW 27th Ave)',
'from' => 'Hialeah Market',
'to' => 'Brickell City Centre',
'wps' => [
[25.8576, -80.2781], [25.8450, -80.2700], [25.8320, -80.2620],
[25.8190, -80.2540], [25.8060, -80.2460], [25.7930, -80.2380],
[25.7800, -80.2300], [25.7680, -80.2220], [25.7617, -80.1918],
],
],
'B' => [
'name' => 'MIA Airport → Miami Beach (E por NW 36th St)',
'from' => 'Miami International Airport',
'to' => 'South Beach',
'wps' => [
[25.7959, -80.2870], [25.7959, -80.2650], [25.7940, -80.2430],
[25.7880, -80.2210], [25.7820, -80.1990], [25.7760, -80.1770],
[25.7700, -80.1550], [25.7640, -80.1330], [25.7617, -80.1320],
],
],
'C' => [
'name' => 'Kendall → Hialeah (N por FL Turnpike) — DESVÍO POSIBLE',
'from' => 'Kendall Drive',
'to' => 'Hialeah',
'wps' => [
[25.7090, -80.3560], [25.7230, -80.3420], [25.7370, -80.3280],
[25.7510, -80.3140], [25.7650, -80.3000], [25.7790, -80.2860],
[25.7930, -80.2720], [25.8070, -80.2580], [25.8210, -80.2440],
],
'deviation_start' => 3,
'deviation_path' => [
[25.7520, -80.2900], [25.7540, -80.2650],
[25.7560, -80.2400], [25.7580, -80.2150],
],
],
'D' => [
'name' => 'Aventura → Downtown Miami (S por Biscayne Blvd)',
'from' => 'Aventura Mall',
'to' => 'Downtown Miami',
'wps' => [
[25.9565, -80.1425], [25.9380, -80.1410], [25.9190, -80.1395],
[25.9000, -80.1380], [25.8810, -80.1365], [25.8620, -80.1350],
[25.8240, -80.1320], [25.7900, -80.1800], [25.7750, -80.1900],
],
],
'E' => [
'name' => 'Doral → Coral Gables (S por Palmetto Expy)',
'from' => 'Doral City Place',
'to' => 'Coral Gables Miracle Mile',
'wps' => [
[25.8196, -80.3556], [25.8100, -80.3400], [25.8000, -80.3250],
[25.7900, -80.3100], [25.7800, -80.2950], [25.7700, -80.2800],
[25.7600, -80.2650],
],
],
];
}
/* ══════════════════════════════════════════════════════════════════════
* CONFIGURACIÓN DE CONDUCTORES (12)
* ['login', 'nombre', ruta, step_inicial, empresa_corp|null]
* route FREE = disponible | route OFF = offline
* ══════════════════════════════════════════════════════════════════════ */
function ab_sim_driver_config() {
return [
['abtest_drv_a1', 'Andrés Pérez', 'A', 0, null],
['abtest_drv_a2', 'Beatriz Torres', 'A', 3, null],
['abtest_drv_b1', 'Carlos Méndez', 'B', 0, null],
['abtest_drv_b2', 'Diana López', 'B', 5, 'TechCorp'],
['abtest_drv_c1', 'Eduardo Ruiz', 'C', 0, null],
['abtest_drv_d1', 'Felipe Vargas', 'D', 0, 'LogiCorp'],
['abtest_drv_e1', 'Gabriela Suárez', 'E', 0, null],
['abtest_drv_f1', 'Hugo Ramírez', 'FREE', null, null],
['abtest_drv_f2', 'Isabel Castro', 'FREE', null, null],
['abtest_drv_f3', 'Julián Morales', 'FREE', null, null],
['abtest_drv_f4', 'Karen Reyes', 'FREE', null, null],
['abtest_drv_f5', 'Luis Herrera', 'OFF', null, null],
];
}
function ab_sim_haversine($lat1, $lng1, $lat2, $lng2) {
$R = 6371;
$dLat = deg2rad($lat2 - $lat1);
$dLng = deg2rad($lng2 - $lng1);
$a = sin($dLat/2)**2 + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLng/2)**2;
return $R * 2 * atan2(sqrt($a), sqrt(1 - $a));
}
function ab_sim_free_positions() {
return [
[4.6700, -74.0550],
[4.6490, -74.0680],
[4.6120, -74.0760],
[4.6320, -74.1100],
];
}
/* ══════════════════════════════════════════════════════════════════════
* ESTADO EN wp_options
* ══════════════════════════════════════════════════════════════════════ */
function ab_sim_state() {
return get_option(AB_SIM_OPT, [
'running' => false,
'deviation' => false,
'drivers' => [],
'last_tick' => 0,
]);
}
function ab_sim_save($s) { update_option(AB_SIM_OPT, $s); }
/* ══════════════════════════════════════════════════════════════════════
* PÁGINA DE ADMINISTRACIÓN (Tools > AB Simulator)
* ══════════════════════════════════════════════════════════════════════ */
add_action('admin_menu', function () {
add_management_page('AB Simulator', 'AB Simulator', 'manage_options', 'ab-sim', 'ab_sim_page');
});
function ab_sim_page() {
if (isset($_POST['ab_sim_act']) && check_admin_referer('ab_sim')) {
$act = sanitize_key($_POST['ab_sim_act']);
if ($act === 'start') ab_sim_start();
if ($act === 'stop') ab_sim_stop();
if ($act === 'reset') ab_sim_reset();
if ($act === 'deviate') ab_sim_activate_deviation();
if ($act === 'tick') ab_sim_tick();
}
$s = ab_sim_state();
$routes = ab_sim_routes();
$running = $s['running'];
?>
<div class="wrap">
<h1>🚗 AutoBooking Simulator</h1>
<div style="background:#fff;padding:20px 24px;margin:16px 0;border:1px solid #ccc;border-radius:6px;">
<p style="font-size:15px;">
Estado: <?php echo $running
? '<strong style="color:#2e7d32">▶ CORRIENDO</strong>'
: '<strong style="color:#555">⏸ DETENIDO</strong>'; ?>
&nbsp;|&nbsp; Último tick: <code><?php echo $s['last_tick'] ? date('H:i:s', $s['last_tick']) : '—'; ?></code>
&nbsp;|&nbsp; Desvío ruta C: <?php echo $s['deviation']
? '<strong style="color:#c62828">⚠ ACTIVO</strong>'
: '<span style="color:#888">inactivo</span>'; ?>
</p>
<form method="post" style="display:inline-block;margin-right:8px;">
<?php wp_nonce_field('ab_sim'); ?>
<input type="hidden" name="ab_sim_act" value="<?php echo $running ? 'stop' : 'start'; ?>">
<button class="button <?php echo $running ? '' : 'button-primary'; ?> button-large">
<?php echo $running ? '⏸ Pausar' : '▶ Iniciar simulación'; ?>
</button>
</form>
<form method="post" style="display:inline-block;margin-right:8px;">
<?php wp_nonce_field('ab_sim'); ?>
<input type="hidden" name="ab_sim_act" value="deviate">
<button class="button button-large" <?php echo !$running ? 'disabled' : ''; ?>>
🔀 Activar desvío (conductor C)
</button>
</form>
<form method="post" style="display:inline-block;margin-right:8px;">
<?php wp_nonce_field('ab_sim'); ?>
<input type="hidden" name="ab_sim_act" value="tick">
<button class="button button-large" <?php echo !$running ? 'disabled' : ''; ?>>
⏭ Avanzar un paso
</button>
</form>
<form method="post" style="display:inline-block;"
onsubmit="return confirm('¿Eliminar todos los datos simulados?')">
<?php wp_nonce_field('ab_sim'); ?>
<input type="hidden" name="ab_sim_act" value="reset">
<button class="button button-large" style="color:#c62828;">🗑 Reset total</button>
</form>
</div>
<?php if (!empty($s['drivers'])): ?>
<h2>Conductores (<?php echo count($s['drivers']); ?>)</h2>
<table class="widefat striped">
<thead><tr><th>ID</th><th>Nombre</th><th>Ruta</th><th>Paso</th><th>Estado DB</th><th>Lat / Lng</th></tr></thead>
<tbody>
<?php foreach ($s['drivers'] as $d):
$u = get_userdata($d['user_id']);
$rkey = $d['route'];
if ($rkey === 'FREE') $rname = '📍 Disponible';
elseif ($rkey === 'OFF') $rname = '🔴 Offline';
else $rname = $routes[$rkey]['name'] ?? $rkey;
?>
<tr>
<td><?php echo $d['user_id']; ?></td>
<td><?php echo esc_html($u ? $u->display_name : '—'); ?></td>
<td><?php echo esc_html($rname); ?></td>
<td><?php echo $d['step'] ?? '—'; ?></td>
<td><?php echo esc_html($d['db_status'] ?? '—'); ?></td>
<td><code><?php echo isset($d['lat']) ? round($d['lat'], 5) . ', ' . round($d['lng'], 5) : '—'; ?></code></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php endif; ?>
<h2 style="margin-top:24px;">Escenarios de prueba</h2>
<table class="widefat">
<thead><tr><th>Escenario</th><th>Qué prueba</th><th>Pasos</th></tr></thead>
<tbody>
<tr>
<td><b>Mapa en vivo</b></td>
<td>Conductores disponibles y activos en el mapa del Command Center</td>
<td>Iniciar → abrir Command Center</td>
</tr>
<tr>
<td><b>Viajes activos</b></td>
<td>7 vehículos moviéndose en tiempo real (cada ~30s)</td>
<td>Iniciar → refrescar mapa cada 30s</td>
</tr>
<tr>
<td><b>Desvío de ruta</b></td>
<td>Conductor C abandona ruta Kennedy→Suba → alerta <code>route_deviation</code> en Command Center</td>
<td>Iniciar → 2 ticks → Activar desvío → ver alertas</td>
</tr>
<tr>
<td><b>Viaje corporativo</b></td>
<td>Diana López (TechCorp) y Felipe Vargas (LogiCorp) con company_id en sus viajes</td>
<td>Iniciar → ver viajes activos con empresa asignada</td>
</tr>
<tr>
<td><b>Avance manual</b></td>
<td>Control paso a paso para debugging de posiciones exactas</td>
<td>Usar "Avanzar un paso" repetidamente</td>
</tr>
</tbody>
</table>
</div>
<?php
}
/* ══════════════════════════════════════════════════════════════════════
* CONTROL
* ══════════════════════════════════════════════════════════════════════ */
function ab_sim_start() {
$s = ab_sim_state();
if (empty($s['drivers'])) {
ab_sim_seed($s);
}
$s['running'] = true;
$s['last_tick'] = time();
ab_sim_save($s);
if (!wp_next_scheduled(AB_SIM_CRON)) {
wp_schedule_event(time(), 'ab_sim_30s', AB_SIM_CRON);
}
}
function ab_sim_stop() {
$s = ab_sim_state();
$s['running'] = false;
ab_sim_save($s);
wp_clear_scheduled_hook(AB_SIM_CRON);
}
function ab_sim_activate_deviation() {
$s = ab_sim_state();
$s['deviation'] = true;
ab_sim_save($s);
}
function ab_sim_reset() {
global $wpdb;
ab_sim_stop();
$s = ab_sim_state();
$uids = array_column($s['drivers'] ?? [], 'user_id');
$extra = get_option('ab_sim_extra_uids', []);
$all = array_unique(array_merge($uids, $extra));
foreach ($all as $uid) {
$trip_ids = $wpdb->get_col($wpdb->prepare(
"SELECT id FROM wp_ab_trips WHERE (driver_id=%d OR passenger_id=%d) AND trip_uuid LIKE 'sim\\_%'",
$uid, $uid
));
if ($trip_ids) {
$in = implode(',', array_map('intval', $trip_ids));
$wpdb->query("DELETE FROM wp_ab_trip_positions WHERE trip_id IN ($in)");
$wpdb->query("DELETE FROM wp_ab_incidents WHERE trip_id IN ($in)");
$wpdb->query("DELETE FROM wp_ab_trips WHERE id IN ($in)");
}
$wpdb->delete('wp_ab_driver_status', ['driver_id' => $uid]);
if (get_user_meta($uid, AB_SIM_MARKER, true) === '1') {
require_once ABSPATH . 'wp-admin/includes/user.php';
wp_delete_user($uid);
}
}
$wpdb->query("DELETE FROM wp_ab_companies WHERE name LIKE '%[SIM]%'");
delete_option(AB_SIM_OPT);
delete_option('ab_sim_extra_uids');
delete_option('ab_sim_company_ids');
}
/* ══════════════════════════════════════════════════════════════════════
* SEED — crear usuarios, empresas y viajes iniciales
* ══════════════════════════════════════════════════════════════════════ */
function ab_sim_seed(&$s) {
global $wpdb;
$routes = ab_sim_routes();
$drv_cfg = ab_sim_driver_config();
$free_pos = ab_sim_free_positions();
$now = current_time('mysql');
/* — 2 empresas institucionales — */
$company_ids = [];
foreach (['TechCorp [SIM]' => 'TechCorp', 'LogiCorp [SIM]' => 'LogiCorp'] as $cname => $ckey) {
$cid = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM wp_ab_companies WHERE name=%s LIMIT 1", $cname
));
if (!$cid) {
$wpdb->insert('wp_ab_companies', [
'name' => $cname,
'email' => strtolower($ckey) . '@sim.local',
'status' => 'active',
'created_at' => $now,
]);
$cid = $wpdb->insert_id;
}
$company_ids[$ckey] = (int) $cid;
}
update_option('ab_sim_company_ids', $company_ids);
/* — 20 pasajeros — */
$pass_names = [
'María García', 'José Rodríguez', 'Ana Martínez', 'Luis González',
'Carmen López', 'Pedro Sánchez', 'Laura Fernández', 'Miguel Torres',
'Isabel Ramírez', 'Carlos Flores', 'Sara Díaz', 'David Morales',
'Elena Jiménez', 'Roberto Ruiz', 'Patricia Herrera','Javier Medina',
'Sofía Delgado', 'Tomás Vargas', 'Valentina Cruz', 'Nicolás Ortiz',
];
$pass_ids = [];
foreach ($pass_names as $i => $pname) {
$login = 'abtest_pass_' . ($i + 1);
$existing = get_user_by('login', $login);
if ($existing) {
$pid = $existing->ID;
} else {
$pid = wp_insert_user([
'user_login' => $login,
'user_pass' => wp_generate_password(),
'display_name' => $pname,
'role' => 'subscriber',
]);
if (is_wp_error($pid)) continue;
update_user_meta($pid, AB_SIM_MARKER, '1');
// Empleados corporativos: índices 16-17 TechCorp, 18-19 LogiCorp
if ($i >= 16 && $i <= 17) update_user_meta($pid, 'ab_company_id', $company_ids['TechCorp']);
if ($i >= 18 && $i <= 19) update_user_meta($pid, 'ab_company_id', $company_ids['LogiCorp']);
}
$pass_ids[] = $pid;
}
update_option('ab_sim_extra_uids', $pass_ids);
/* — 12 conductores — */
$s['drivers'] = [];
$free_idx = 0;
foreach ($drv_cfg as $pi => $cfg) {
[$login, $dname, $route, $step, $corp] = $cfg;
$existing = get_user_by('login', $login);
if ($existing) {
$uid = $existing->ID;
} else {
$uid = wp_insert_user([
'user_login' => $login,
'user_pass' => wp_generate_password(),
'display_name' => $dname,
'role' => 'driver',
]);
if (is_wp_error($uid)) continue;
update_user_meta($uid, AB_SIM_MARKER, '1');
}
// Posición inicial y estado DB
if ($route === 'FREE') {
[$lat, $lng] = $free_pos[$free_idx % count($free_pos)];
$free_idx++;
$db_status = 'available';
} elseif ($route === 'OFF') {
$lat = 4.6500; $lng = -74.0600;
$db_status = 'offline';
} else {
[$lat, $lng] = $routes[$route]['wps'][$step ?? 0];
$db_status = 'on_trip';
}
// Insertar/actualizar wp_ab_driver_status
$exists = $wpdb->get_var($wpdb->prepare(
"SELECT driver_id FROM wp_ab_driver_status WHERE driver_id=%d", $uid
));
if ($exists) {
$wpdb->update('wp_ab_driver_status',
['last_lat' => $lat, 'last_lng' => $lng, 'online' => ($db_status === 'offline') ? 0 : 1, 'last_seen' => $now],
['driver_id' => $uid]
);
} else {
$wpdb->insert('wp_ab_driver_status', [
'driver_id' => $uid,
'last_lat' => $lat,
'last_lng' => $lng,
'online' => ($db_status === 'offline') ? 0 : 1,
'last_seen' => $now,
]);
}
// Crear viaje activo para conductores en ruta
$trip_id = null;
if ($route !== 'FREE' && $route !== 'OFF') {
$pass_id = $pass_ids[$pi] ?? $pass_ids[0];
$wps = $routes[$route]['wps'];
$last_wp = $wps[count($wps) - 1];
$company_id = ($corp && isset($company_ids[$corp])) ? $company_ids[$corp] : null;
$trip_id = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM wp_ab_trips WHERE driver_id=%d AND notes LIKE '%[SIM]%' LIMIT 1", $uid
));
if (!$trip_id) {
$wps = $routes[$route]['wps'];
$last = $wps[count($wps) - 1];
$drv_user = get_userdata($uid);
$pax_user = get_userdata($pass_id);
$wpdb->insert('wp_ab_trips', [
'trip_uuid' => uniqid('sim_', true),
'driver_id' => $uid,
'passenger_id' => $pass_id,
'driver_name' => $drv_user ? $drv_user->display_name : 'Conductor SIM',
'passenger_name' => $pax_user ? $pax_user->display_name : 'Pasajero SIM',
'status' => 'in_progress',
'pickup_lat' => $wps[0][0],
'pickup_lng' => $wps[0][1],
'dropoff_lat' => $last[0],
'dropoff_lng' => $last[1],
'pickup_time' => $now,
'created_at' => $now,
]);
$trip_id = $wpdb->insert_id;
}
if ($trip_id) {
$wpdb->insert('wp_ab_trip_positions', [
'trip_id' => $trip_id,
'lat' => $lat,
'lng' => $lng,
'ts' => $now,
]);
}
}
$s['drivers'][] = [
'user_id' => $uid,
'route' => $route,
'step' => $step ?? 0,
'trip_id' => $trip_id,
'db_status' => $db_status,
'lat' => $lat,
'lng' => $lng,
'deviated' => false,
];
}
}
/* ══════════════════════════════════════════════════════════════════════
* TICK — avanzar todos los vehículos un paso
* ══════════════════════════════════════════════════════════════════════ */
add_action(AB_SIM_CRON, 'ab_sim_tick');
function ab_sim_tick() {
global $wpdb;
$s = ab_sim_state();
if (!$s['running']) return;
$routes = ab_sim_routes();
$now = current_time('mysql');
foreach ($s['drivers'] as &$d) {
$route = $d['route'];
if ($route === 'FREE') {
// Micromovimiento para conductores disponibles
$d['lat'] += (mt_rand(-4, 4) * 0.0001);
$d['lng'] += (mt_rand(-4, 4) * 0.0001);
$wpdb->update('wp_ab_driver_status',
['lat' => $d['lat'], 'lng' => $d['lng'], 'updated_at' => $now],
['user_id' => $d['user_id']]
);
continue;
}
if ($route === 'OFF') continue;
$wps = $routes[$route]['wps'];
$dev_start = $routes[$route]['deviation_start'] ?? PHP_INT_MAX;
$dev_path = $routes[$route]['deviation_path'] ?? [];
$is_deviating = $s['deviation'] && $route === 'C' && $d['step'] >= $dev_start;
if ($is_deviating) {
$di = $d['step'] - $dev_start;
if (isset($dev_path[$di])) {
[$d['lat'], $d['lng']] = $dev_path[$di];
// Primera vez que desvía: registrar separación pasajero-conductor
if (!$d['deviated'] && $d['trip_id']) {
$sep = $wps[$dev_start - 1] ?? $wps[0]; // último punto en ruta normal
$trip_row = $wpdb->get_row($wpdb->prepare(
"SELECT dropoff_lat, dropoff_lng, passenger_name FROM wp_ab_trips WHERE id = %d", $d['trip_id']
));
$dist_dest = ($trip_row && $trip_row->dropoff_lat)
? ab_sim_haversine($sep[0], $sep[1], $trip_row->dropoff_lat, $trip_row->dropoff_lng)
: 0;
$wpdb->insert('wp_ab_incidents', [
'trip_id' => $d['trip_id'],
'user_id' => $d['user_id'],
'type' => 'separacion_sospechosa',
'description' => sprintf(
'[SIM] Conductor abandonó ruta. Pasajero %s último punto conocido: %.5f, %.5f (a %.1f km del destino contratado). Conductor ahora en: %.5f, %.5f.',
$trip_row->passenger_name ?? 'desconocido',
$sep[0], $sep[1], $dist_dest,
$d['lat'], $d['lng']
),
'lat' => $d['lat'],
'lng' => $d['lng'],
'status' => 'open',
'created_at' => $now,
]);
$d['deviated'] = true;
}
$d['step']++;
} else {
// Agotó la ruta de desvío — cerrar viaje con alerta de separación fuera del destino
if ($d['trip_id']) {
$trip_row = $wpdb->get_row($wpdb->prepare(
"SELECT dropoff_lat, dropoff_lng, passenger_name FROM wp_ab_trips WHERE id = %d", $d['trip_id']
));
$sep = $wps[$dev_start - 1] ?? $wps[0];
if ($trip_row && $trip_row->dropoff_lat) {
$dist = ab_sim_haversine($sep[0], $sep[1], $trip_row->dropoff_lat, $trip_row->dropoff_lng);
$wpdb->insert('wp_ab_incidents', [
'trip_id' => $d['trip_id'],
'user_id' => $d['user_id'],
'type' => 'separacion_destino_incorrecto',
'description' => sprintf(
'[SIM] Viaje cerrado fuera del destino. Pasajero %s separado a %.1f km del destino contratado (%.5f, %.5f). Punto de separación: %.5f, %.5f.',
$trip_row->passenger_name ?? 'desconocido',
$dist,
$trip_row->dropoff_lat, $trip_row->dropoff_lng,
$sep[0], $sep[1]
),
'lat' => $sep[0],
'lng' => $sep[1],
'status' => 'open',
'created_at' => $now,
]);
}
$wpdb->update('wp_ab_trips',
['status' => 'completed', 'completed_at' => $now],
['id' => $d['trip_id']]
);
$d['trip_id'] = null;
}
$d['step'] = 0;
$d['deviated'] = false;
$d['db_status'] = 'available';
[$d['lat'], $d['lng']] = $wps[0];
$wpdb->update('wp_ab_driver_status',
['last_lat' => $d['lat'], 'last_lng' => $d['lng'], 'online' => 1, 'last_seen' => $now],
['driver_id' => $d['user_id']]
);
continue;
}
} else {
$next = $d['step'] + 1;
if ($next >= count($wps)) {
// Llegó al destino — verificar si coincide con el punto contratado
if ($d['trip_id']) {
$trip_row = $wpdb->get_row($wpdb->prepare(
"SELECT dropoff_lat, dropoff_lng, passenger_name FROM wp_ab_trips WHERE id = %d", $d['trip_id']
));
if ($trip_row && $trip_row->dropoff_lat) {
$dist = ab_sim_haversine($d['lat'], $d['lng'], $trip_row->dropoff_lat, $trip_row->dropoff_lng);
if ($dist > 0.5) {
$wpdb->insert('wp_ab_incidents', [
'trip_id' => $d['trip_id'],
'user_id' => $d['user_id'],
'type' => 'separacion_fuera_destino',
'description' => sprintf(
'[SIM] Viaje completado a %.1f km del destino contratado. Pasajero %s separado en: %.5f, %.5f. Destino original: %.5f, %.5f.',
$dist,
$trip_row->passenger_name ?? 'desconocido',
$d['lat'], $d['lng'],
$trip_row->dropoff_lat, $trip_row->dropoff_lng
),
'lat' => $d['lat'],
'lng' => $d['lng'],
'status' => 'open',
'created_at' => $now,
]);
}
}
$wpdb->update('wp_ab_trips',
['status' => 'completed', 'completed_at' => $now],
['id' => $d['trip_id']]
);
$d['trip_id'] = null;
}
$d['step'] = 0;
$d['deviated'] = false;
$d['db_status'] = 'available';
[$d['lat'], $d['lng']] = $wps[0];
$wpdb->update('wp_ab_driver_status',
['last_lat' => $d['lat'], 'last_lng' => $d['lng'], 'online' => 1, 'last_seen' => $now],
['driver_id' => $d['user_id']]
);
continue;
}
$d['step'] = $next;
[$d['lat'], $d['lng']] = $wps[$next];
}
$wpdb->update('wp_ab_driver_status',
['last_lat' => $d['lat'], 'last_lng' => $d['lng'], 'last_seen' => $now],
['driver_id' => $d['user_id']]
);
if ($d['trip_id']) {
$wpdb->insert('wp_ab_trip_positions', [
'trip_id' => $d['trip_id'],
'lat' => $d['lat'],
'lng' => $d['lng'],
'ts' => $now,
]);
}
}
unset($d);
$s['last_tick'] = time();
ab_sim_save($s);
}
/* ══════════════════════════════════════════════════════════════════════
* CRON — intervalo de 30 segundos
* ══════════════════════════════════════════════════════════════════════ */
add_filter('cron_schedules', function ($sc) {
$sc['ab_sim_30s'] = ['interval' => 30, 'display' => 'Cada 30s (AutoBooking Sim)'];
return $sc;
});
register_deactivation_hook(__FILE__, function () {
wp_clear_scheduled_hook(AB_SIM_CRON);
});