Files
gps-frontend/app/Models/Tracks_copy.php
meusinfirmary 3e6ea0b3d4 update
2025-07-23 04:44:20 +07:00

601 lines
23 KiB
PHP
Executable File

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use App\Helper;
use App\Models\Vehicles;
class Tracks_copy extends Model
{
const T_TRACKS = "t_gps_tracks";
const T_TRACKS_ADDR = "t_gps_tracks_address";
const STTS_REVERSE_GEO_SC = 1;
const STTS_REVERSE_GEO_NOT = 2;
const STTS_REVERSE_GEO_ER = 3;
const STTS_REVERSE_GEO_LOST = 4;
const STTS_EN_IDLING = 1;
const STTS_EN_MOVING = 2;
const STTS_EN_STOPING = 3;
const STTS_IGNITION_DFT = 0;
const STTS_IGNITION_ON = 1;
const STTS_IGNITION_OFF = 2;
const STTS_IGNITION_LOW = 3;
const STTS_IGNITION_HIGH = 4;
const STTS_GPS_DFT = 0; // default
const STTS_GPS_ON = 1;
const STTS_GPS_OFF = 2;
// 1=>no signal, 2=>extremely weak signal 3=>very weak signal, 4=>good signal, 5=>strong signal
const STTS_GSM_DFT = 0; // default
const STTS_GSM_NO_SIGNAL = 1;
const STTS_GSM_BAD_SIGNAL = 2;
const STTS_GSM_WEAK_SIGNAL = 3;
const STTS_GSM_GOOD_SIGNAL = 4;
const STTS_GSM_STRONG_SIGNAL = 5;
// type source
const SOURCE_GPS_TRACKER = 1;
const SOURCE_SMARTPHONE = 2;
/**
* perlu tambahin filter untuk melihat tracking yang sesuai dengan vehiclenya aja
* karena ada case:
* device sudah sampai dilokasi pengantaran dan dimatikan, ketika dipakai lagi data tracking yang lama kelihatan dan loncat
* untuk mengatasi itu di service_tracking juga perlu validasi tambahan, entah seperti apa caranya
*/
public static function listCurrentTracks($filter = [])
{
$now = time();
$params = [];
$query = "SELECT";
$query .=
" v.id as vid,v.device_id,v.name as vhc_name,c.name as vhc_cat_name,t.name as vhc_type_name";
$query .= " ,v.nopol1,v.nopol2,v.nopol3,vd.fvhc_img";
$query .= " ,v.is_track_holiday,v.track_sch_d,v.track_sch_h,vd.speed_limit,v.crt as vhc_crt";
$query .= " ,client.id as client_group_id,client.c_name as client_group_name";
$query .= " ,tr.ignition,tr.stts_engine,tr.stts_gps,tr.stts_gsm";
$query .= " ,tr.pre_milleage,tr.sum_milleage,tr.vhc_milleage,v.sum_milleage AS vhc_sum_milleage_1";
$query .=
" ,tr.id AS lst_master_id,tr.latitude AS lst_lat,tr.longitude AS lst_lng,tr.speed AS lst_speed,tr.orientation AS lst_orientation";
$query .= " ,tr.crt AS lst_loc_crt,tr.crt_d AS lst_loc_crt_d,tr.crt_s AS lst_loc_crt_s";
$query .=
" ,tr_addr.master_id AS lst_addr_master_id,tr_addr.country_text AS lst_country_text,tr_addr.state_text AS lst_state_text,tr_addr.city_text AS lst_city_text";
$query .=
" ,tr_addr.district_text AS lst_district_text,tr_addr.village_text AS lst_village_text,tr_addr.postcode AS lst_postcode";
$query .= " ,tr_addr.streets AS lst_streets,tr_addr.fulladdress AS lst_fulladdress";
if (isset($filter["active_rates"])) {
$query .=
",rate.vdr_id as rate_vdr_id,rate.vhc_type as rate_vhc_type,rate.origin_prov as rate_origin_prov,rate.dest_city as rate_dest_city,rate.dest_district as rate_dest_district";
$query .=
",rate.fast_time as rate_fast_time,rate.long_time as rate_long_time,rate.sell_kg as rate_sell_kg,rate.sell_cbm as rate_sell_cbm,rate.sell_ftl as rate_sell_ftl";
}
if (isset($filter["get_order_data"])) {
$query .=
",ord.id as ord_id,ord.code as ord_code,ord.status as ord_stts,ord.crt as ord_crt,ord_pck.pck_name as ord_pck_name,ord_pck.pck_addr as ord_pck_addr,ord_drop.drop_name as ord_drop_name,ord_drop.drop_addr as ord_drop_addr";
$query .=
",(SELECT nmKotamadyaKel FROM t_region WHERE kodeKab = ord_pck.pck_ktid LIMIT 1) ord_pck_ktname, (SELECT nmKotamadyaKel FROM t_region WHERE kodeKab = ord_drop.drop_ktid LIMIT 1) ord_drop_ktname";
$query .=
",ord_drv.drv_name as ord_drv_name,ord_drv.drv_phone_val as ord_drv_phone_val,ord_drv.drv_phone2_val as ord_drv_phone2_val,ord_drv.drv_addr as ord_drv_addr";
$query .= ",ord_c.c_name as ord_c_name,ord_c.c_pt_name as ord_c_pt_name";
}
$query .= " FROM t_vehicles AS v";
$query .= " INNER JOIN t_vehicles_detail AS vd ON v.id = vd.vid";
$query .= " INNER JOIN t_vehicles_types AS t ON v.type_id = t.id";
$query .= " INNER JOIN t_vehicles_cats AS c ON v.cat_id = c.id";
$query .= " LEFT JOIN t_users AS vendor ON v.vendor_id = vendor.id";
$query .= " LEFT JOIN t_clients AS client ON vendor.client_group_id = client.id";
$query .= " LEFT JOIN t_gps_tracks_rltm AS tr ON tr.vhc_id = v.id";
$query .=
" LEFT JOIN " .
self::T_TRACKS_ADDR .
" AS tr_addr ON tr.latitude = tr_addr.lat and tr.longitude = tr_addr.lng";
if (isset($filter["active_rates"])) {
$query .= " INNER JOIN t_conf_rates as rate ON v.vendor_id = rate.vdr_id";
}
if (isset($filter["get_order_data"])) {
$query .= " LEFT JOIN t_orders as ord ON v.ord_id = ord.id";
$query .= " LEFT JOIN t_orders_pickups as ord_pck ON v.ord_id = ord_pck.ord_id";
$query .= " LEFT JOIN t_orders_drops as ord_drop ON v.ord_id = ord_drop.ord_id";
$query .= " LEFT JOIN t_orders_drivers as ord_drv ON v.ord_id = ord_drv.ord_id";
$query .= " LEFT JOIN t_orders_clients as ord_c ON v.ord_id = ord_c.ord_id";
}
$query .= " WHERE v.dlt is null";
$query .= " AND tr.latitude != 0 AND tr.latitude is not null";
if (isset($filter["status"])) {
$query .= " AND v.status = ?";
$params[] = $filter["status"];
}
if (isset($filter["is_in_ord"])) {
$query .= " AND v.is_in_ord = ?";
$params[] = $filter["is_in_ord"];
}
if (isset($filter["nopol1"]) && isset($filter["nopol2"]) && isset($filter["nopol3"])) {
$query .= " AND v.nopol1 = ? AND v.nopol2 = ? AND v.nopol3 = ?";
array_push($params, $filter["nopol1"], $filter["nopol2"], $filter["nopol3"]);
}
if (isset($filter["vid"])) {
$query .= " AND v.id = ?";
$params[] = $filter["vid"];
}
if (isset($filter["vids"])) {
if ($filter["vids"] && count($filter["vids"]) > 0) {
$binds_vids = "";
foreach ($filter["vids"] as $k => $v) {
$binds_vids .= "?,";
$params[] = $v;
}
if (substr($binds_vids, -1) === ",") {
$binds_vids = substr($binds_vids, 0, -1);
}
$query .= " AND v.id IN ($binds_vids)";
} else {
$query .= " AND v.id = ?";
$params[] = 0;
}
}
if (isset($filter["own_by_vdr_id"])) {
$query .= " AND v.vendor_id = ?";
$params[] = $filter["own_by_vdr_id"];
}
if (isset($filter["active_rates"])) {
$query .=
" AND rate.vdr_id != 0 AND rate.dest_city != 0 AND rate.is_active = " . ConfRates::IS_ACTIVE;
$query .=
" AND rate.origin_prov = ? AND (rate.dest_city = ? OR rate.dest_district = ?) AND rate.sell_ftl = ? AND rate.long_time = ?";
array_push(
$params,
$filter["active_rates"]->origin_prov,
$filter["active_rates"]->dest_city,
$filter["active_rates"]->dest_district,
$filter["active_rates"]->sell_ftl,
$filter["active_rates"]->long_time
);
}
if (isset($filter["prefer_truck_type"])) {
$query .= " AND v.type_id = ?";
$params[] = $filter["prefer_truck_type"];
}
if (isset($filter["get_order_data"])) {
if (isset($filter["client_id"])) {
$query .= " AND ord_c.c_id = ?";
$params[] = $filter["client_id"];
}
}
if (isset($filter["company"])) {
$query .= " AND client.id = ?";
$params[] = $filter["company"];
}
$query .= " GROUP BY v.id";
$query .= " ORDER BY tr.crt_d DESC";
$query .= " LIMIT 500;";
$list = DB::select($query, $params);
foreach ($list as $_list) {
$_query =
" SELECT SUM(pre_milleage) as vhc_sum_milleage FROM " . self::T_TRACKS . " WHERE vhc_id = ?";
$_query = DB::select($_query, [$_list->vid]);
$_list->vhc_sum_milleage = $_query[0]->vhc_sum_milleage;
$_query =
"
SELECT
crt_s as lst_idle_at
FROM
" .
self::T_TRACKS .
"
WHERE
stts_engine = " .
self::STTS_EN_IDLING .
"
AND vhc_id = ?
AND crt_s >= (
SELECT
crt_s
FROM
" .
self::T_TRACKS .
"
WHERE
stts_engine IN (
" .
self::STTS_EN_STOPING .
",
" .
self::STTS_EN_MOVING .
")
AND vhc_id = ?
AND crt_s <= ?
ORDER BY
id DESC
LIMIT 1
)
ORDER BY
id ASC
LIMIT 1";
$_query = DB::select($_query, [$_list->vid, $_list->vid, $_list->lst_loc_crt_s]);
$_list->lst_idle_at = count($_query) == 0 ? "" : $_query[0]->lst_idle_at;
$_query =
"SELECT crt_s as lst_stop_at FROM " .
self::T_TRACKS .
" WHERE stts_engine = " .
self::STTS_EN_STOPING .
" AND vhc_id = ? AND crt_s >= (
SELECT crt_s FROM " .
self::T_TRACKS .
" WHERE stts_engine IN (" .
self::STTS_EN_IDLING .
"," .
self::STTS_EN_MOVING .
") AND vhc_id = ? AND crt_s <= ? ORDER BY id DESC LIMIT 1
) ORDER BY id ASC LIMIT 1 ";
$_query = DB::select($_query, [$_list->vid, $_list->vid, $_list->lst_loc_crt_s]);
$_list->lst_stop_at = count($_query) == 0 ? "" : $_query[0]->lst_stop_at;
$_query =
" SELECT COUNT(id) as lst_heartbeat FROM " .
self::T_TRACKS .
" WHERE vhc_id = ? AND action = 'heartbeat' AND crt BETWEEN " .
($now - 10 * 60) .
" AND " .
$now .
" LIMIT 1";
$_query = DB::select($_query, [$_list->vid]);
$_list->lst_heartbeat = count($_query) == 0 ? "" : $_query[0]->lst_heartbeat;
}
return $list;
}
public static function lastMoveTracks($vid, $filter = [])
{
$params = [];
$query = "SELECT";
$query .= " v.id as vid,v.device_id,v.nopol1,v.nopol2,v.nopol3";
$query .= " ,tr.id as master_id,tr.latitude,tr.longitude,tr.speed,tr.orientation, tr.crt_d_format";
$query .= " ,tr.crt AS lst_loc_crt,tr.crt_d AS lst_loc_crt_d,tr.crt_s AS lst_loc_crt_s";
$query .= " ,tr.ignition,tr.stts_engine";
$query .= " ,tr.pre_milleage,tr.sum_milleage,tr.vhc_milleage,v.sum_milleage AS vhc_sum_milleage_1";
$query .= " ,addr.master_id AS addr_master_id,addr.crt AS addr_loc_crt";
$query .=
" ,addr.country_text,addr.state_text,addr.city_text,addr.district_text,addr.village_text,addr.postcode,addr.streets,addr.postcode,addr.fulladdress";
$query .= " FROM t_vehicles AS v";
$query .= " INNER JOIN " . self::T_TRACKS . " AS tr ON v.id = tr.vhc_id";
$query .= " LEFT JOIN " . self::T_TRACKS_ADDR . " AS addr ON tr.id = addr.master_id";
$query .= " WHERE v.dlt IS NULL AND v.id = ?";
$params[] = $vid;
$query .= " AND tr.latitude IS NOT NULL AND tr.longitude IS NOT NULL";
if (isset($filter["start_date"], $filter["end_date"])) {
$query .= " AND tr.crt_d BETWEEN ? AND ?";
$params[] = $filter["start_date"];
$params[] = $filter["end_date"];
}
if (isset($filter["start_at"])) {
$query .= " AND tr.crt > ?";
$params[] = $filter["start_at"];
}
$query .= " ORDER BY tr.crt_d DESC";
$limit = isset($filter["limit"]) && $filter["limit"] > 500 ? $filter["limit"] : 3000;
$query .= " LIMIT ?";
$params[] = $limit;
$raw = DB::select($query, $params);
$rawAscCleaned = self::removeSpeedSandwich($raw);
$filtered = self::filterJumps($rawAscCleaned);
$combined = $filtered;
usort($combined, fn($a, $b) => $b->lst_loc_crt_d <=> $a->lst_loc_crt_d);
$final = array_slice($combined, 0, 500);
return $final;
}
public static function haversineDistance($lat1, $lon1, $lat2, $lon2)
{
$earthRadius = 6371000; // meters
$lat1 = deg2rad($lat1);
$lon1 = deg2rad($lon1);
$lat2 = deg2rad($lat2);
$lon2 = deg2rad($lon2);
$dlat = $lat2 - $lat1;
$dlon = $lon2 - $lon1;
$a = sin($dlat / 2) ** 2 + cos($lat1) * cos($lat2) * sin($dlon / 2) ** 2;
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
return $earthRadius * $c;
}
private static function removeSpeedSandwich($data)
{
$cleaned = [];
for ($i = 0; $i < count($data); $i++) {
$next = $data[$i + 1] ?? null; // lebih lama
$curr = $data[$i];
$prev = $data[$i - 1] ?? null; // lebih baru
// Logika dibalik: prev = lebih baru, next = lebih lama
if ($prev && $next && $prev->speed > 5 && $curr->speed < 5 && $next->speed > 5) {
continue;
}
$cleaned[] = $curr;
}
return $cleaned;
}
public static function filterJumps(
$points,
$maxDistance = 150,
$maxTimeGap = 20,
$maxSpeed = 40,
$maxHardJump = 800,
$maxIdleJump = 1000,
$maxRealSpeed = 150
) {
// km/h
$filtered = [];
$last = null;
$start = null;
foreach ($points as $p) {
if (!$last) {
$filtered[] = $p;
$last = $p;
$start = $p;
continue;
}
$distance = self::haversineDistance(
$last->latitude,
$last->longitude,
$p->latitude,
$p->longitude
);
$t1 = strtotime($last->lst_loc_crt);
$t2 = strtotime($p->lst_loc_crt);
$timeGap = $t2 - $t1;
$speed = $timeGap > 0 ? $distance / $timeGap : 0;
$realSpeedKmh = $speed * 3.6;
// Tambahan: jarak terhadap titik awal
$jumpFromStart = self::haversineDistance(
$start->latitude,
$start->longitude,
$p->latitude,
$p->longitude
);
// 🚫 Lompatan keras (langsung > 800 m)
if ($distance > $maxHardJump) {
continue;
}
// 🚫 Lompat saat idle (dianggap tidak wajar jika dua titik idle dan lompat jauh)
if ($distance > $maxIdleJump && $p->speed < 5 && $last->speed < 5) {
continue;
}
// 🚫 Lompat dalam waktu pendek dan speed terlalu tinggi
if ($timeGap <= $maxTimeGap && $speed > $maxSpeed) {
continue;
}
// 🚫 Speed tidak masuk akal
if ($realSpeedKmh > $maxRealSpeed) {
continue;
}
// 🚫 Lompat dari titik awal terlalu jauh (anomaly GPS loncat)
if ($jumpFromStart > 10000 && $p->speed < 5) {
continue;
} // >10 km dari area awal tapi status idle
$filtered[] = $p;
$last = $p;
}
return $filtered;
}
public static function nearestInCircle($lat, $lng, $rad, $filter = [])
{
$earth_rad = Helper::EARTH_RADIUS_M;
$t_gps_tracks = self::T_TRACKS;
$select_select = "";
$join_join = "";
$where_where = "";
$params = [
"lat1" => $lat,
"lat2" => $lat,
"lng" => $lng,
"v_status" => Vehicles::STTS_ACTIVE,
"v_is_in_ord" => Vehicles::IN_ORD_NO,
"rad" => $rad,
];
if (isset($filter["active_rates"])) {
$select_select .=
",rate.vdr_id as rate_vdr_id,rate.vhc_type as rate_vhc_type,rate.origin_prov as rate_origin_prov,rate.dest_city as rate_dest_city,rate.dest_district as rate_dest_district";
$select_select .=
",rate.fast_time as rate_fast_time,rate.long_time as rate_long_time,rate.sell_kg as rate_sell_kg,rate.sell_cbm";
}
if (isset($filter["active_rates"])) {
// $join_join .= " INNER JOIN t_conf_rates as rate ON v.type_id = rate.vhc_type";
$join_join .= " INNER JOIN t_conf_rates as rate ON v.vendor_id = rate.vdr_id";
}
if (isset($filter["active_rates"])) {
// v1
// $where_where .= " AND rate.vdr_id != 0 AND rate.dest_city != 0 AND rate.dest_district != 0";
// $where_where .= " AND rate.origin_prov = :origin_prov AND (rate.dest_city = :dest_city OR rate.dest_district = :dest_district) AND rate.sell_kg = :sell_kg AND rate.sell_cbm = :sell_cbm AND rate.long_time = :long_time";
// v2
$where_where .=
" AND rate.vdr_id != 0 AND rate.dest_city != 0 AND rate.is_active = " . ConfRates::IS_ACTIVE;
$where_where .=
" AND rate.origin_prov = :origin_prov AND (rate.dest_city = :dest_city OR rate.dest_district = :dest_district) AND rate.sell_ftl = :sell_ftl AND rate.long_time = :long_time";
$params["origin_prov"] = $filter["active_rates"]->origin_prov;
$params["dest_city"] = $filter["active_rates"]->dest_city;
$params["dest_district"] = $filter["active_rates"]->dest_district;
// v1
// $params['sell_kg'] = $filter['active_rates']->sell_kg;
// $params['sell_cbm'] = $filter['active_rates']->sell_cbm;
// v2
$params["sell_ftl"] = $filter["active_rates"]->sell_ftl;
$params["long_time"] = $filter["active_rates"]->long_time;
}
if (isset($filter["prefer_truck_type"])) {
$where_where .= " AND v.type_id = :prefer_truck_type";
$params["prefer_truck_type"] = $filter["prefer_truck_type"];
}
$query = "SELECT v.*,tr.latitude,tr.longitude,tr.id as master_id
,u.first_name as vendor_name,u.phone as vendor_phone,u.phone_code as vendor_phone_code,u.email as vendor_mail,u.fulladdress as vendor_addr
,( $earth_rad * acos( cos( radians( :lat1 ) ) * cos( radians( tr.latitude ) )
* cos( radians( tr.longitude ) - radians( :lng ) ) + sin( radians( :lat2 ) ) * sin(radians( tr.latitude )) ) ) AS distance
$select_select
FROM $t_gps_tracks as tr
INNER JOIN t_vehicles as v ON tr.vhc_id = v.id
INNER JOIN t_users as u ON v.vendor_id = u.id
-- get last updated row from many rows
LEFT JOIN ( SELECT MAX(crt) max_crt, vhc_id FROM $t_gps_tracks WHERE latitude is not null AND longitude is not null GROUP BY vhc_id ORDER BY crt DESC ) AS tr1 ON (v.id = tr1.vhc_id)
$join_join
WHERE v.dlt is null
AND v.status = :v_status
AND v.is_in_ord = :v_is_in_ord
AND tr.latitude is not null
AND tr.longitude != 0
-- get last updated row from many rows
AND tr.crt = tr1.max_crt
$where_where
GROUP BY tr.vhc_id
HAVING distance <= :rad
-- ORDER BY distance ASC
ORDER BY tr.crt DESC
;";
return DB::select($query, $params);
}
public static function addTracks($data)
{
$id = DB::table(self::T_TRACKS)->insertGetId($data);
return $id;
}
public static function addTracksAddr($data)
{
$id = DB::table(self::T_TRACKS_ADDR)->insertGetId($data);
return $id;
}
public static function listLogsGps($filter = [])
{
$params = [];
$where_where = "";
$limit = "";
if (isset($filter["crt_greater_than"])) {
$where_where .= " AND crt > ?";
$params[] = $filter["crt_greater_than"];
}
if (isset($filter["limit"])) {
$limit .= " LIMIT ?";
$params[] = $filter["limit"];
}
return DB::select(
"SELECT
*
FROM " .
self::T_TRACKS .
" as tr
WHERE device_id is not null
AND device_id != 0
" .
$where_where .
"
ORDER BY tr.crt DESC
" .
$limit .
"
",
$params
);
}
public static function gpsLocsAddr($filter = [])
{
$params = [];
$select = "";
$join = "";
$where = "";
$order_by = "";
$group_by = "";
$limit = "";
if (isset($filter["source"])) {
$where .= " AND tr.source = ?";
$params[] = $filter["source"];
}
if (isset($filter["vhc_id"])) {
$where .= " AND tr.vhc_id = ?";
$params[] = $filter["vhc_id"];
}
if (isset($filter["drv_id"])) {
$where .= " AND tr.drv_id = ?";
$params[] = $filter["drv_id"];
}
if (isset($filter["group_by"])) {
$group_by .= " GROUP BY " . $filter["group_by"];
}
if (isset($filter["order_by"])) {
$order_by .= " ORDER BY " . $filter["order_by"];
}
if (isset($filter["limit"])) {
$limit .= " LIMIT " . $filter["limit"];
}
return DB::select(
"SELECT
tr.*
,tr_addr.country_id,tr_addr.country_text,tr_addr.state_id,tr_addr.state_text,tr_addr.city_id,tr_addr.city_text
,tr_addr.district_id,tr_addr.district_text,tr_addr.village_id,tr_addr.village_text,tr_addr.postcode,tr_addr.streets,tr_addr.fulladdress
,tr_addr.stts_reverse_geo
$select
FROM t_gps_tracks as tr
LEFT JOIN t_gps_tracks_address as tr_addr ON tr.id = tr_addr.master_id
$join
WHERE tr.latitude is not null AND tr.longitude != 0
$where
$group_by
$order_by
$limit
;",
$params
);
}
}