traccarService = $traccarService; } public function mount() { // Set default date range (last 7 days) $this->endDate = now()->format('Y-m-d'); $this->startDate = now()->subDays(7)->format('Y-m-d'); // Select all user devices by default $deviceQuery = Device::where('user_id', Auth::id()); // Check if device groups have users relationship (safely handle missing relationship) if (method_exists(\App\Models\DeviceGroup::class, 'users')) { $deviceQuery->orWhereHas('group.users', function($subQuery) { $subQuery->where('user_id', Auth::id()); }); } $this->selectedDevices = $deviceQuery->pluck('id')->toArray(); } public function generateReport() { $this->validate([ 'selectedDevices' => 'required|array|min:1', 'startDate' => 'required|date', 'endDate' => 'required|date|after_or_equal:startDate', ]); try { switch ($this->reportType) { case 'route': $this->generateRouteReport(); break; case 'summary': $this->generateSummaryReport(); break; case 'events': $this->generateEventsReport(); break; case 'stops': $this->generateStopsReport(); break; case 'trips': $this->generateTripsReport(); break; } session()->flash('success', 'Report generated successfully!'); } catch (\Exception $e) { session()->flash('error', 'Failed to generate report: ' . $e->getMessage()); } } private function generateRouteReport() { $startDateTime = Carbon::parse($this->startDate)->startOfDay(); $endDateTime = Carbon::parse($this->endDate)->endOfDay(); $this->routeData = []; foreach ($this->selectedDevices as $deviceId) { $device = Device::find($deviceId); if (!$device) continue; $positions = Position::where('device_id', $deviceId) ->whereBetween('device_time', [$startDateTime, $endDateTime]) ->orderBy('device_time') ->get(); if ($positions->count() > 0) { $route = [ 'device' => $device, 'positions' => $positions, 'start_time' => $positions->first()->device_time, 'end_time' => $positions->last()->device_time, 'total_distance' => $this->calculateTotalDistance($positions), 'max_speed' => $positions->max('speed') ?? 0, 'avg_speed' => $positions->avg('speed') ?? 0, ]; if ($this->includeStops) { $route['stops'] = $this->detectStops($positions); } $this->routeData[] = $route; } } } private function generateSummaryReport() { $startDateTime = Carbon::parse($this->startDate)->startOfDay(); $endDateTime = Carbon::parse($this->endDate)->endOfDay(); $this->summaryStats = []; foreach ($this->selectedDevices as $deviceId) { $device = Device::find($deviceId); if (!$device) continue; $positions = Position::where('device_id', $deviceId) ->whereBetween('device_time', [$startDateTime, $endDateTime]) ->get(); $events = Event::where('device_id', $deviceId) ->whereBetween('event_time', [$startDateTime, $endDateTime]) ->get(); $totalDistance = $this->calculateTotalDistance($positions); $totalTime = $positions->count() > 1 ? $positions->last()->device_time->diffInMinutes($positions->first()->device_time) : 0; $this->summaryStats[] = [ 'device' => $device, 'total_distance' => $totalDistance, 'total_time' => $totalTime, 'avg_speed' => $totalTime > 0 ? ($totalDistance / ($totalTime / 60)) : 0, 'max_speed' => $positions->max('speed') ?? 0, 'total_events' => $events->count(), 'overspeed_events' => $events->where('type', 'deviceOverspeed')->count(), 'online_time' => $this->calculateOnlineTime($positions), 'fuel_consumption' => $this->calculateFuelConsumption($positions), ]; } } private function generateEventsReport() { $startDateTime = Carbon::parse($this->startDate)->startOfDay(); $endDateTime = Carbon::parse($this->endDate)->endOfDay(); $this->eventsData = Event::with(['device', 'geofence']) ->whereIn('device_id', $this->selectedDevices) ->whereBetween('event_time', [$startDateTime, $endDateTime]) ->orderBy('event_time', 'desc') ->get() ->groupBy(function($event) { return $this->groupByDate ? $event->event_time->format('Y-m-d') : $event->device->name; }); } private function generateStopsReport() { $startDateTime = Carbon::parse($this->startDate)->startOfDay(); $endDateTime = Carbon::parse($this->endDate)->endOfDay(); $this->stopsData = []; foreach ($this->selectedDevices as $deviceId) { $device = Device::find($deviceId); if (!$device) continue; $positions = Position::where('device_id', $deviceId) ->whereBetween('device_time', [$startDateTime, $endDateTime]) ->orderBy('device_time') ->get(); $stops = $this->detectStops($positions); if (!empty($stops)) { $this->stopsData[] = [ 'device' => $device, 'stops' => $stops, 'total_stops' => count($stops), 'total_stop_time' => array_sum(array_column($stops, 'duration')), ]; } } } private function generateTripsReport() { $startDateTime = Carbon::parse($this->startDate)->startOfDay(); $endDateTime = Carbon::parse($this->endDate)->endOfDay(); $this->reportData = []; foreach ($this->selectedDevices as $deviceId) { $device = Device::find($deviceId); if (!$device) continue; $positions = Position::where('device_id', $deviceId) ->whereBetween('device_time', [$startDateTime, $endDateTime]) ->orderBy('device_time') ->get(); $trips = $this->detectTrips($positions); $this->reportData[] = [ 'device' => $device, 'trips' => $trips, 'total_trips' => count($trips), 'total_distance' => array_sum(array_column($trips, 'distance')), 'total_duration' => array_sum(array_column($trips, 'duration')), ]; } } private function calculateTotalDistance($positions) { $totalDistance = 0; $previousPosition = null; foreach ($positions as $position) { if ($previousPosition) { $distance = $this->calculateDistance( $previousPosition->latitude, $previousPosition->longitude, $position->latitude, $position->longitude ); $totalDistance += $distance; } $previousPosition = $position; } return round($totalDistance, 2); } private function calculateDistance($lat1, $lon1, $lat2, $lon2) { $earthRadius = 6371; // kilometers $dLat = deg2rad($lat2 - $lat1); $dLon = deg2rad($lon2 - $lon1); $a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * sin($dLon/2) * sin($dLon/2); $c = 2 * atan2(sqrt($a), sqrt(1-$a)); return $earthRadius * $c; } private function detectStops($positions) { $stops = []; $currentStop = null; $speedThreshold = 2; // km/h foreach ($positions as $position) { $speed = $position->speed ?? 0; if ($speed <= $speedThreshold) { if (!$currentStop) { $currentStop = [ 'start_time' => $position->device_time, 'latitude' => $position->latitude, 'longitude' => $position->longitude, 'address' => $position->address, ]; } } else { if ($currentStop) { $currentStop['end_time'] = $position->device_time; $currentStop['duration'] = $currentStop['start_time']->diffInMinutes($currentStop['end_time']); if ($currentStop['duration'] >= $this->minStopDuration) { $stops[] = $currentStop; } $currentStop = null; } } } return $stops; } private function detectTrips($positions) { $trips = []; $currentTrip = null; $speedThreshold = 2; // km/h foreach ($positions as $position) { $speed = $position->speed ?? 0; if ($speed > $speedThreshold) { if (!$currentTrip) { $currentTrip = [ 'start_time' => $position->device_time, 'start_latitude' => $position->latitude, 'start_longitude' => $position->longitude, 'start_address' => $position->address, 'positions' => [$position], ]; } else { $currentTrip['positions'][] = $position; } } else { if ($currentTrip && count($currentTrip['positions']) > 1) { $lastPosition = end($currentTrip['positions']); $currentTrip['end_time'] = $lastPosition->device_time; $currentTrip['end_latitude'] = $lastPosition->latitude; $currentTrip['end_longitude'] = $lastPosition->longitude; $currentTrip['end_address'] = $lastPosition->address; $currentTrip['duration'] = $currentTrip['start_time']->diffInMinutes($currentTrip['end_time']); $currentTrip['distance'] = $this->calculateTotalDistance(collect($currentTrip['positions'])); $currentTrip['max_speed'] = collect($currentTrip['positions'])->max('speed') ?? 0; unset($currentTrip['positions']); // Remove positions to save memory $trips[] = $currentTrip; $currentTrip = null; } } } return $trips; } private function calculateOnlineTime($positions) { if ($positions->count() < 2) return 0; return $positions->first()->device_time->diffInMinutes($positions->last()->device_time); } private function calculateFuelConsumption($positions) { // This is a simplified calculation - in reality you'd use more sophisticated methods $totalDistance = $this->calculateTotalDistance($positions); $avgConsumption = 8; // liters per 100km (default) return round(($totalDistance / 100) * $avgConsumption, 2); } public function exportReport() { // This would implement actual export functionality session()->flash('success', 'Report export started. You will receive a download link shortly.'); } public function render() { $deviceQuery = Device::where('user_id', Auth::id()); // Check if device groups have users relationship (safely handle missing relationship) if (method_exists(\App\Models\DeviceGroup::class, 'users')) { $deviceQuery->orWhereHas('group.users', function($subQuery) { $subQuery->where('user_id', Auth::id()); }); } $devices = $deviceQuery->get(); // Get recent events for the current user $recentEvents = Event::with(['device', 'position']) ->whereHas('device', function($q) { $q->where('user_id', Auth::id()); }) ->orderBy('event_time', 'desc') ->limit(10) ->get(); return view('livewire.reports-and-history', [ 'devices' => $devices, 'recentEvents' => $recentEvents, ]); } }