where('device_uid', $deviceUid)->first(); if (! $device) { return; } $device->update(['last_seen_at' => now(), 'status' => 'active']); foreach ($readings as $reading) { IotTelemetry::create([ 'business_id' => $businessId, 'device_id' => $device->id, 'equipment_id' => $device->equipment_id, 'metric' => $reading['metric'], 'value' => $reading['value'], 'unit' => $reading['unit'] ?? null, 'recorded_at' => $reading['recorded_at'] ?? now(), 'raw_payload' => $reading, ]); $this->evaluateRules($businessId, $device, $reading['metric'], (float) $reading['value']); } } protected function evaluateRules(int $businessId, IotDevice $device, string $metric, float $value): void { $rules = IotPredictiveRule::where('business_id', $businessId) ->where('is_active', true) ->where(function ($q) use ($device) { $q->whereNull('equipment_id')->orWhere('equipment_id', $device->equipment_id); }) ->where('metric', $metric) ->get(); foreach ($rules as $rule) { if (! $this->thresholdBreached($value, (float) $rule->threshold, $rule->operator)) { continue; } $alert = IotAlert::create([ 'business_id' => $businessId, 'device_id' => $device->id, 'equipment_id' => $device->equipment_id, 'alert_type' => 'threshold_breach', 'severity' => 'warning', 'message' => "Metric {$metric} breached threshold ({$value} {$rule->operator} {$rule->threshold})", 'threshold_value' => $rule->threshold, 'actual_value' => $value, 'triggered_at' => now(), ]); if ($rule->auto_work_order && $device->equipment_id) { $woId = $this->createPredictiveWorkOrder($businessId, $device->equipment_id, $alert); $alert->update(['work_order_id' => $woId]); } } } protected function thresholdBreached(float $value, float $threshold, string $operator): bool { return match ($operator) { 'gt' => $value > $threshold, 'lt' => $value < $threshold, 'gte' => $value >= $threshold, 'lte' => $value <= $threshold, 'eq' => abs($value - $threshold) < 0.0001, default => false, }; } protected function createPredictiveWorkOrder(int $businessId, int $equipmentId, IotAlert $alert): ?int { if (! DB::getSchemaBuilder()->hasTable('mt_work_orders')) { return null; } return DB::table('mt_work_orders')->insertGetId([ 'business_id' => $businessId, 'equipment_id' => $equipmentId, 'title' => 'Predictive: '.$alert->message, 'type' => 'corrective', 'priority' => 'high', 'status' => 'open', 'description' => 'Auto-created from IoT alert #'.$alert->id, 'created_at' => now(), 'updated_at' => now(), ]); } }