isInControl( (float) $data['measured_value'], isset($data['usl']) ? (float) $data['usl'] : null, isset($data['lsl']) ? (float) $data['lsl'] : null ); return SpcMeasurement::create(array_merge($data, [ 'business_id' => $businessId, 'measured_at' => $data['measured_at'] ?? now(), 'in_control' => $inControl, 'recorded_by' => $data['recorded_by'] ?? auth()->id(), ])); } public function isInControl(float $value, ?float $usl, ?float $lsl): bool { if ($usl !== null && $value > $usl) { return false; } if ($lsl !== null && $value < $lsl) { return false; } return true; } public function characteristicSummary(int $businessId, string $characteristic, int $days = 30): array { $measurements = SpcMeasurement::forBusiness($businessId) ->where('characteristic_name', $characteristic) ->where('measured_at', '>=', now()->subDays($days)) ->orderBy('measured_at') ->get(); if ($measurements->isEmpty()) { return ['count' => 0, 'mean' => 0, 'min' => 0, 'max' => 0, 'out_of_control' => 0]; } $values = $measurements->pluck('measured_value')->map(fn ($v) => (float) $v); return [ 'count' => $measurements->count(), 'mean' => round($values->avg(), 4), 'min' => round($values->min(), 4), 'max' => round($values->max(), 4), 'out_of_control' => $measurements->where('in_control', false)->count(), 'cpk_estimate' => $this->estimateCpk($values, $measurements->first()), ]; } protected function estimateCpk($values, SpcMeasurement $sample): ?float { $usl = $sample->usl; $lsl = $sample->lsl; if ($usl === null || $lsl === null || $values->count() < 2) { return null; } $mean = $values->avg(); $std = $this->stdDev($values->all()); if ($std <= 0) { return null; } $cpu = ($usl - $mean) / (3 * $std); $cpl = ($mean - $lsl) / (3 * $std); return round(min($cpu, $cpl), 3); } protected function stdDev(array $values): float { $n = count($values); if ($n < 2) { return 0; } $mean = array_sum($values) / $n; $variance = array_sum(array_map(fn ($v) => ($v - $mean) ** 2, $values)) / ($n - 1); return sqrt($variance); } }