import { cobbStandardPerformance } from '../mockData/cobbStandard.ts'; /** * Analyze trend of numerical data over a window * @param data Array of numerical values * @param windowSize Number of recent data points to analyze * @returns Trend description in Indonesian */ export function analyzeTrend(data: number[], windowSize = 7): string { if (!data || data.length < 2) { return 'tidak cukup data'; } // Filter out invalid values (null, undefined, NaN, 0) const validData = data.filter((val) => val != null && !isNaN(val) && val !== 0); if (validData.length < 2) { return 'tidak cukup data'; } // Take last N values const recentData = validData.slice(-Math.min(windowSize, validData.length)); if (recentData.length < 2) { return 'tidak cukup data'; } // Calculate simple linear regression slope const n = recentData.length; const xMean = (n - 1) / 2; // Mean of indices 0, 1, 2, ..., n-1 const yMean = recentData.reduce((sum, val) => sum + val, 0) / n; let numerator = 0; let denominator = 0; for (let i = 0; i < n; i++) { numerator += (i - xMean) * ((recentData[i] ?? 0) - yMean); denominator += Math.pow(i - xMean, 2); } const slope = denominator !== 0 ? numerator / denominator : 0; // Calculate relative slope (as percentage of mean) const relativeSlope = yMean !== 0 ? (slope / yMean) * 100 : 0; // Determine trend based on relative slope // Threshold: >2% = increasing, <-2% = decreasing, else stable if (relativeSlope > 2) { return 'meningkat'; } else if (relativeSlope < -2) { return 'menurun'; } else { return 'stabil'; } } /** * Get cycle phase based on day of cycle * @param day Current day of cycle (1-based) * @returns Phase name */ export function getCyclePhase(day: number): string { if (day <= 14) { return 'Brooding'; } else if (day <= 28) { return 'Growth'; } else { return 'Finisher'; } } /** * Get Cobb standard performance metrics for a given day * @param day Day of cycle (1-based) * @returns Object with FCR and weight, or null if not available */ export function getCobbStandardForDay(day: number): { fcr: number; weight: number } | null { const standard = cobbStandardPerformance.find((entry) => entry.day === day); if (standard) { return { fcr: standard.fcr, // Weight targets live in page builders / CP707 knowledge; FCR row has no weight column here. weight: 0, }; } return null; } /** * Calculate deviation between actual and target values * @param actual Actual value * @param target Target value * @returns Object with absolute and percentage deviation */ export function calculateDeviation( actual: number, target: number ): { absolute: number; percent: number } { const absolute = actual - target; const percent = target !== 0 ? (absolute / target) * 100 : 0; return { absolute, percent }; } /** * Determine environmental status based on readings and cycle phase * @param temp Temperature in Celsius * @param humidity Humidity percentage * @param ammonia Ammonia in ppm * @param day Current day of cycle * @returns Status description in Indonesian */ export function getEnvironmentalStatus( temp: number, humidity: number, ammonia: number, day: number ): string { const issues: string[] = []; // Age-appropriate temperature ranges // Younger chicks need warmer temperatures let optimalTempMin: number; let optimalTempMax: number; if (day <= 7) { optimalTempMin = 32; optimalTempMax = 35; } else if (day <= 14) { optimalTempMin = 30; optimalTempMax = 32; } else if (day <= 21) { optimalTempMin = 28; optimalTempMax = 30; } else { optimalTempMin = 26; optimalTempMax = 28; } // Check temperature if (temp < optimalTempMin) { issues.push('suhu terlalu rendah'); } else if (temp > optimalTempMax) { issues.push('suhu terlalu tinggi'); } // Check humidity (optimal: 50-70%) if (humidity < 50) { issues.push('kelembapan rendah'); } else if (humidity > 70) { issues.push('kelembapan tinggi'); } // Check ammonia (safe: <25ppm, warning: 25-50ppm, critical: >50ppm) if (ammonia > 50) { issues.push('amonia kritis'); } else if (ammonia > 25) { issues.push('amonia tinggi'); } if (issues.length === 0) { return 'optimal'; } else if (issues.length === 1) { return issues[0] ?? 'optimal'; } else { return issues.join(', '); } } /** * Analyze feed consumption trend * @param feedHistory Array of daily feed consumption (in sacks) * @param _currentDay Current day of cycle (reserved for future use) * @returns Trend description in Indonesian */ export function analyzeFeedTrend(feedHistory: number[], _currentDay: number): string { if (!feedHistory || feedHistory.length < 3) { return 'tidak cukup data'; } // Get last 7 days of feed data const recentFeed = feedHistory.slice(-7); // Rough expectation: feed consumption should increase gradually as chickens grow // Early days: 0.5-1 sack/1000 birds/day // Mid cycle: 1-2 sacks/1000 birds/day // Late cycle: 2-3 sacks/1000 birds/day // This is a simplified heuristic - in real deployment, this would be calibrated // based on actual farm data and population size // For now, just analyze if consumption is increasing (expected as birds grow) const trend = analyzeTrend(recentFeed, 7); if (trend === 'meningkat') { return 'sesuai pertumbuhan'; } else if (trend === 'menurun') { return 'menurun (perlu perhatian)'; } else { return 'stabil'; } }