ultimatepos/Modules/Maintenance/Services/IotIngestionService.php

103 lines
3.7 KiB
PHP

<?php
namespace Modules\Maintenance\Services;
use Illuminate\Support\Facades\DB;
use Modules\Maintenance\Models\IotAlert;
use Modules\Maintenance\Models\IotDevice;
use Modules\Maintenance\Models\IotPredictiveRule;
use Modules\Maintenance\Models\IotTelemetry;
class IotIngestionService
{
public function ingest(int $businessId, string $deviceUid, array $readings): void
{
$device = IotDevice::where('business_id', $businessId)->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(),
]);
}
}