setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } 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'])) { // Prevent any output before JSON response ob_clean(); header('Content-Type: application/json'); try { switch ($_POST['action']) { case 'get_tags': $stmt = $pdo->prepare("SELECT DISTINCT name FROM 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_pivot_data': // Decode JSON tags parameter properly $selectedTagsJson = $_POST['tags'] ?? '[]'; // Check if it's already an array or needs to be decoded if (is_string($selectedTagsJson)) { $selectedTags = json_decode($selectedTagsJson, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception('Invalid tags JSON: ' . json_last_error_msg()); } } else { $selectedTags = $selectedTagsJson; } $startDate = $_POST['start_date'] ?? ''; $endDate = $_POST['end_date'] ?? ''; $groupBy = $_POST['group_by'] ?? 'hour'; $aggregation = $_POST['aggregation'] ?? 'avg'; $limit = (int)($_POST['limit'] ?? 1000); // Validate selectedTags if (empty($selectedTags) || !is_array($selectedTags)) { throw new Exception('No tags selected or invalid tags format'); } // Validate limit - ensure it's a positive integer if ($limit <= 0 || $limit > 50000) { $limit = 1000; } // Build dynamic SQL based on grouping $dateFormat = ''; switch ($groupBy) { case 'minute': $dateFormat = "DATE_FORMAT(h.TimeStamp, '%Y-%m-%d %H:%i:00')"; break; case 'hour': $dateFormat = "DATE_FORMAT(h.TimeStamp, '%Y-%m-%d %H:00:00')"; break; case 'day': $dateFormat = "DATE_FORMAT(h.TimeStamp, '%Y-%m-%d 00:00:00')"; break; case 'month': $dateFormat = "DATE_FORMAT(h.TimeStamp, '%Y-%m-01 00:00:00')"; break; default: $dateFormat = "DATE_FORMAT(h.TimeStamp, '%Y-%m-%d %H:00:00')"; } // Build aggregation function $aggFunction = strtoupper($aggregation); if (!in_array($aggFunction, ['AVG', 'SUM', 'MIN', 'MAX', 'COUNT'])) { $aggFunction = 'AVG'; } // Create placeholders for IN clause $placeholders = str_repeat('?,', count($selectedTags) - 1) . '?'; // Build the SQL query - LIMIT added directly to SQL string $sql = "SELECT $dateFormat as time_group, n.name as tag_name, $aggFunction(h.Value) as value, COUNT(*) as data_points FROM historicaldata h INNER JOIN id_names n ON h.ID = n.idnumber WHERE n.name IN ($placeholders)"; $params = $selectedTags; if (!empty($startDate)) { $sql .= " AND h.TimeStamp >= ?"; $params[] = $startDate; } if (!empty($endDate)) { $sql .= " AND h.TimeStamp <= ?"; $params[] = $endDate; } $sql .= " GROUP BY time_group, n.name ORDER BY time_group ASC, n.name ASC"; // Add LIMIT directly to the SQL string (not as a parameter) $sql .= " LIMIT " . $limit; // Debug logging error_log("Selected tags: " . print_r($selectedTags, true)); error_log("Final SQL: " . $sql); error_log("Parameters: " . print_r($params, true)); $stmt = $pdo->prepare($sql); $stmt->execute($params); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); // Transform data for pivot table $pivotData = []; $timeGroups = []; $actualTagNames = []; // Track actual tag names from results foreach ($results as $row) { $timeGroup = $row['time_group']; $tagName = $row['tag_name']; $value = (float)$row['value']; if (!in_array($timeGroup, $timeGroups)) { $timeGroups[] = $timeGroup; } if (!in_array($tagName, $actualTagNames)) { $actualTagNames[] = $tagName; } if (!isset($pivotData[$timeGroup])) { $pivotData[$timeGroup] = ['time_group' => $timeGroup]; } $pivotData[$timeGroup][$tagName] = $value; } // Fill missing values with null for actual tag names that returned data foreach ($pivotData as &$row) { foreach ($actualTagNames as $tagName) { if (!isset($row[$tagName])) { $row[$tagName] = null; } } } echo json_encode([ 'success' => true, 'data' => array_values($pivotData), 'timeGroups' => $timeGroups, 'tagNames' => $actualTagNames, // Use actual tag names that returned data 'totalRows' => count($pivotData), 'rawResultCount' => count($results), 'query_info' => [ 'group_by' => $groupBy, 'aggregation' => $aggregation, 'date_range' => [$startDate, $endDate], 'selected_tags' => $selectedTags, 'actual_tags' => $actualTagNames, 'limit_applied' => $limit, 'sql_query' => $sql ] ]); break; default: throw new Exception('Invalid action specified'); } } catch (Exception $e) { echo json_encode(['success' => false, 'error' => $e->getMessage()]); } exit; // Always exit after AJAX response } ?>
Dynamic Data Analysis & Visualization