PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); } catch (PDOException $e) { if (isset($_POST['action'])) { header('Content-Type: application/json'); echo json_encode( [ 'success' => false, 'error' => 'Database connection failed: ' . $e->getMessage(), ] ); exit; } die("Connection failed: " . $e->getMessage()); } // Handle AJAX requests if (isset($_POST['action'])) { // Clear any previous output and set proper headers while (ob_get_level()) { ob_end_clean(); } ob_start(); header('Content-Type: application/json'); try { switch ($_POST['action']) { case 'get_tags': $stmt = $pdo->prepare( "SELECT DISTINCT name FROM dbo.id_names WHERE name IS NOT NULL AND name <> '' ORDER BY name" ); $stmt->execute(); $tags = $stmt->fetchAll(PDO::FETCH_COLUMN); echo json_encode( [ 'success' => true, 'tags' => $tags, ] ); break; case 'get_recent_data': $selectedTagsJson = $_POST['tags'] ?? '[]'; $selectedTags = is_string($selectedTagsJson) ? json_decode($selectedTagsJson, true) : $selectedTagsJson; $hours = (int) ($_POST['hours'] ?? 24); if ($hours < 1) { $hours = 1; } if (empty($selectedTags) || !is_array($selectedTags)) { throw new Exception('No tags selected'); } $placeholders = implode(',', array_fill(0, count($selectedTags), '?')); $cutoff = (new DateTimeImmutable()) ->modify(sprintf('-%d hours', $hours)) ->format('Y-m-d H:i:s'); $sql = " SELECT TOP 10000 h.TimeStamp, n.name AS tag_name, h.Value FROM dbo.historicaldata AS h INNER JOIN dbo.id_names AS n ON h.ID = n.idnumber WHERE n.name IN ($placeholders) AND h.TimeStamp >= ? ORDER BY h.TimeStamp DESC, n.name ASC "; $params = array_merge($selectedTags, [$cutoff]); $stmt = $pdo->prepare($sql); $stmt->execute($params); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode( [ 'success' => true, 'data' => $results, 'count' => count($results), ] ); break; case 'analyze_anomalies': $selectedTagsJson = $_POST['tags'] ?? '[]'; $selectedTags = is_string($selectedTagsJson) ? json_decode($selectedTagsJson, true) : $selectedTagsJson; $sensitivity = (float) ($_POST['sensitivity'] ?? 2.0); $lookbackHours = (int) ($_POST['lookback_hours'] ?? 168); $analysisHours = (int) ($_POST['analysis_hours'] ?? 24); if ($lookbackHours < 1) { $lookbackHours = 1; } if ($analysisHours < 1) { $analysisHours = 1; } if ($lookbackHours <= $analysisHours) { $lookbackHours = $analysisHours + 1; } if (empty($selectedTags) || !is_array($selectedTags)) { throw new Exception('No tags selected'); } $anomalies = []; $now = new DateTimeImmutable(); $analysisStart = $now->modify(sprintf('-%d hours', $analysisHours)); $baselineStart = $now->modify(sprintf('-%d hours', $lookbackHours)); $analysisStartString = $analysisStart->format('Y-m-d H:i:s'); $baselineStartString = $baselineStart->format('Y-m-d H:i:s'); $baselineEndString = $analysisStartString; foreach ($selectedTags as $tagName) { $baselineStmt = $pdo->prepare( " SELECT h.Value FROM dbo.historicaldata AS h INNER JOIN dbo.id_names AS n ON h.ID = n.idnumber WHERE n.name = ? AND h.TimeStamp >= ? AND h.TimeStamp < ? AND h.Value IS NOT NULL ORDER BY h.TimeStamp ASC " ); $baselineStmt->execute( [ $tagName, $baselineStartString, $baselineEndString, ] ); $baselineData = $baselineStmt->fetchAll(PDO::FETCH_COLUMN); $recentStmt = $pdo->prepare( " SELECT h.Value, h.TimeStamp FROM dbo.historicaldata AS h INNER JOIN dbo.id_names AS n ON h.ID = n.idnumber WHERE n.name = ? AND h.TimeStamp >= ? AND h.Value IS NOT NULL ORDER BY h.TimeStamp ASC " ); $recentStmt->execute( [ $tagName, $analysisStartString, ] ); $recentResults = $recentStmt->fetchAll(PDO::FETCH_ASSOC); if (count($baselineData) < 10 || count($recentResults) < 5) { continue; } $baselineValues = array_map('floatval', $baselineData); $mean = array_sum($baselineValues) / count($baselineValues); $variance = 0; foreach ($baselineValues as $value) { $variance += pow($value - $mean, 2); } $stdDev = sqrt($variance / (count($baselineValues) - 1)); if ($stdDev < 0.001) { continue; } $upperThreshold = $mean + ($sensitivity * $stdDev); $lowerThreshold = $mean - ($sensitivity * $stdDev); foreach ($recentResults as $point) { $value = (float) $point['Value']; $timestamp = $point['TimeStamp']; if ($value > $upperThreshold || $value < $lowerThreshold) { $deviation = abs($value - $mean) / $stdDev; $severity = 'medium'; if ($deviation > $sensitivity * 2) { $severity = 'critical'; } elseif ($deviation > $sensitivity * 1.5) { $severity = 'high'; } $message = sprintf( 'Value %.2f is %.1fσ from normal (%s)', $value, $deviation, sprintf('%.2f ± %.2f', $mean, $stdDev) ); $anomalies[] = [ 'tag_name' => $tagName, 'timestamp' => $timestamp, 'value' => $value, 'expected_range' => [$lowerThreshold, $upperThreshold], 'baseline_mean' => $mean, 'baseline_stddev' => $stdDev, 'deviation_factor' => $deviation, 'severity' => $severity, 'message' => $message, ]; } } } usort( $anomalies, function ($a, $b) { $severityOrder = [ 'low' => 1, 'medium' => 2, 'high' => 3, 'critical' => 4, ]; $severityDiff = ($severityOrder[$b['severity']] ?? 2) - ($severityOrder[$a['severity']] ?? 2); if ($severityDiff !== 0) { return $severityDiff; } return strtotime($b['timestamp']) - strtotime($a['timestamp']); } ); $criticalCount = 0; $highCount = 0; $mediumCount = 0; foreach ($anomalies as $item) { switch ($item['severity']) { case 'critical': $criticalCount++; break; case 'high': $highCount++; break; case 'medium': $mediumCount++; break; } } echo json_encode( [ 'success' => true, 'anomalies' => $anomalies, 'analysis_summary' => [ 'total_anomalies' => count($anomalies), 'critical_count' => $criticalCount, 'high_count' => $highCount, 'medium_count' => $mediumCount, 'analysis_period' => $analysisHours . ' hours', 'baseline_period' => $lookbackHours . ' hours', 'sensitivity_level' => $sensitivity, ], ] ); break; case 'get_tag_statistics': $tagName = $_POST['tag_name'] ?? ''; $hours = (int) ($_POST['hours'] ?? 168); if ($hours < 1) { $hours = 1; } if (empty($tagName)) { throw new Exception('No tag specified'); } $cutoff = (new DateTimeImmutable()) ->modify(sprintf('-%d hours', $hours)) ->format('Y-m-d H:i:s'); $stmt = $pdo->prepare( " SELECT COUNT(*) AS data_points, AVG(CAST(h.Value AS float)) AS mean_value, STDEVP(CAST(h.Value AS float)) AS std_deviation, MIN(h.Value) AS min_value, MAX(h.Value) AS max_value, VARP(CAST(h.Value AS float)) AS variance FROM dbo.historicaldata AS h INNER JOIN dbo.id_names AS n ON h.ID = n.idnumber WHERE n.name = ? AND h.TimeStamp >= ? AND h.Value IS NOT NULL " ); $stmt->execute([$tagName, $cutoff]); $stats = $stmt->fetch(PDO::FETCH_ASSOC); echo json_encode( [ 'success' => true, 'statistics' => $stats, 'tag_name' => $tagName, 'analysis_period' => $hours . ' hours', ] ); break; default: throw new Exception('Invalid action specified'); } } catch (Exception $e) { echo json_encode( [ 'success' => false, 'error' => $e->getMessage(), ] ); } catch (Error $e) { echo json_encode( [ 'success' => false, 'error' => 'PHP Error: ' . $e->getMessage(), ] ); } ob_end_flush(); exit; } ?>
Real-Time Equipment Monitoring & Predictive Analytics