PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ] ); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); } // Handle AJAX request for autocomplete if (isset($_GET['action']) && $_GET['action'] === 'autocomplete') { $query = isset($_GET['query']) ? trim($_GET['query']) : ''; $suggestions = []; if (!empty($query)) { try { // Make sure we're using the same column name as the main query $stmt = $pdo->prepare(" SELECT TOP 20 name FROM dbo.id_names WHERE name LIKE :query ORDER BY name "); $stmt->execute([':query' => '%' . $query . '%']); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($results as $row) { $suggestions[] = $row['name']; } } catch (PDOException $e) { // Handle error silently for AJAX error_log("Autocomplete error: " . $e->getMessage()); } } header('Content-Type: application/json'); echo json_encode($suggestions); exit; } // Handle search parameters $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : 'single_name'; $searchName = isset($_GET['name']) ? trim($_GET['name']) : ''; // Handle multiple tag dropdowns $selectedTags = []; for ($i = 1; $i <= 5; $i++) { $tagParam = "tag{$i}"; if (isset($_GET[$tagParam]) && !empty(trim($_GET[$tagParam]))) { $selectedTags[] = trim($_GET[$tagParam]); } } $multipleNames = implode(', ', $selectedTags); // For backward compatibility $startDate = isset($_GET['start_date']) ? $_GET['start_date'] : ''; $endDate = isset($_GET['end_date']) ? $_GET['end_date'] : ''; // Normalize HTML datetime-local (YYYY-MM-DDTHH:MM[/SS]) to SQL Server format $normalizeDateTime = function ($value) { if (empty($value)) { return ''; } $patterns = [ 'Y-m-d\TH:i:s', 'Y-m-d\TH:i' ]; foreach ($patterns as $pattern) { $dt = DateTime::createFromFormat($pattern, $value); if ($dt instanceof DateTime) { return $dt->format('Y-m-d H:i:s'); } } // Fallback: let SQL Server attempt conversion unchanged return $value; }; $startDate = $normalizeDateTime($startDate); $endDate = $normalizeDateTime($endDate); $limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 100; $showTrend = isset($_GET['show_trend']) ? true : false; // Change from fixed value to variable with default $timeInterval = isset($_GET['time_interval']) ? (int)$_GET['time_interval'] : 30; // Default 30 seconds // Add validation if ($timeInterval < 1) { $timeInterval = 1; // Minimum 1 second } $results = []; $searchTitle = ''; $totalCount = 0; $tagNames = []; $organizedResults = []; // Only search if required fields are provided $canSearch = false; switch ($searchType) { case 'single_name': $canSearch = !empty($searchName); break; case 'multiple_names': $canSearch = !empty($selectedTags); // Use selectedTags instead of multipleNames break; } if ($canSearch) { try { // Base queries - no time interval filtering $sql = " SELECT h.ID, h.Value, h.TimeStamp, COALESCE(n.name, CONCAT('ID_', h.ID)) AS name FROM dbo.historicaldata h LEFT JOIN dbo.id_names n ON h.ID = n.idnumber WHERE 1 = 1 "; $countSql = " SELECT COUNT(*) FROM dbo.historicaldata h LEFT JOIN dbo.id_names n ON h.ID = n.idnumber WHERE 1 = 1 "; $params = []; // Build query based on search type switch ($searchType) { case 'single_name': $sql .= " AND n.name = :name"; $countSql .= " AND n.name = :name"; $params[':name'] = $searchName; $searchTitle = $searchName; break; case 'multiple_names': if (!empty($selectedTags)) { $placeholders = []; for ($i = 0; $i < count($selectedTags); $i++) { $placeholder = ':name' . $i; $placeholders[] = $placeholder; $params[$placeholder] = $selectedTags[$i]; } $placeholderString = implode(',', $placeholders); $sql .= " AND n.name IN ($placeholderString)"; $countSql .= " AND n.name IN ($placeholderString)"; $searchTitle = "Multiple Tags (" . count($selectedTags) . "): " . implode(', ', array_slice($selectedTags, 0, 3)) . (count($selectedTags) > 3 ? '...' : ''); } break; } // Add date filters if (!empty($startDate)) { $sql .= " AND h.TimeStamp >= :start_date"; $countSql .= " AND h.TimeStamp >= :start_date"; $params[':start_date'] = $startDate; } if (!empty($endDate)) { $sql .= " AND h.TimeStamp <= :end_date"; $countSql .= " AND h.TimeStamp <= :end_date"; $params[':end_date'] = $endDate; } // Add time interval filtering if ($timeInterval > 1) { if ($timeInterval >= 60) { // For minute intervals, align to minute boundaries $minutes = $timeInterval / 60; $sql .= " AND DATEPART(MINUTE, h.TimeStamp) % :minute_interval = 0 AND DATEPART(SECOND, h.TimeStamp) = 0 "; $countSql .= " AND DATEPART(MINUTE, h.TimeStamp) % :minute_interval = 0 AND DATEPART(SECOND, h.TimeStamp) = 0 "; $params[':minute_interval'] = (int)$minutes; } else { $sql .= " AND DATEPART(SECOND, h.TimeStamp) % :time_interval = 0"; $countSql .= " AND DATEPART(SECOND, h.TimeStamp) % :time_interval = 0"; $params[':time_interval'] = $timeInterval; } } // Get total count $countStmt = $pdo->prepare($countSql); $countStmt->execute($params); $totalCount = $countStmt->fetchColumn(); // Add ordering and limit if ($showTrend) { $sql .= " ORDER BY h.TimeStamp ASC OFFSET 0 ROWS FETCH NEXT :limit ROWS ONLY "; } else { $sql .= " ORDER BY h.TimeStamp DESC OFFSET 0 ROWS FETCH NEXT :limit ROWS ONLY "; } $params[':limit'] = $limit; $stmt = $pdo->prepare($sql); // Bind all parameters foreach ($params as $key => $value) { if ($key === ':limit') { $stmt->bindValue($key, $value, PDO::PARAM_INT); } else { $stmt->bindValue($key, $value); } } $stmt->execute(); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); // Organize results by timestamp if (!empty($results)) { foreach ($results as $row) { $timestamp = $row['TimeStamp']; $tagName = $row['name']; $value = $row['Value']; if (!isset($organizedResults[$timestamp])) { $organizedResults[$timestamp] = ['timestamp' => $timestamp]; } $organizedResults[$timestamp][$tagName] = $value; if (!in_array($tagName, $tagNames)) { $tagNames[] = $tagName; } } // Sort timestamps if ($showTrend && $searchType == 'single_name') { ksort($organizedResults); } else { krsort($organizedResults); } } } catch (PDOException $e) { echo "
| Timestamp | |
|---|---|
No data found for the specified search criteria.