PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); } catch (PDOException $e) { die("Connection failed: " . $e->getMessage()); } $isAutocomplete = isset($_GET['action']) && $_GET['action'] === 'autocomplete'; if ($isAutocomplete) { $query = isset($_GET['query']) ? trim($_GET['query']) : ''; $suggestions = []; if ($query !== '') { try { $stmt = $pdo->prepare(" SELECT TOP 20 name FROM dbo.id_names WHERE name LIKE :query ORDER BY name "); $stmt->execute([':query' => '%' . $query . '%']); $rows = $stmt->fetchAll(); foreach ($rows as $row) { if (!empty($row['name'])) { $suggestions[] = $row['name']; } } } catch (PDOException $e) { error_log('Autocomplete error: ' . $e->getMessage()); } } header('Content-Type: application/json'); echo json_encode($suggestions); exit; } $allTags = []; try { $tagStmt = $pdo->prepare(" SELECT DISTINCT name FROM dbo.id_names WHERE name <> '' ORDER BY name "); $tagStmt->execute(); $allTags = $tagStmt->fetchAll(); } catch (PDOException $e) { error_log('Tag preload error: ' . $e->getMessage()); } $searchType = isset($_GET['search_type']) ? $_GET['search_type'] : 'single_name'; $searchName = isset($_GET['name']) ? trim($_GET['name']) : ''; $selectedTags = []; for ($i = 1; $i <= 5; $i++) { $param = "tag{$i}"; if (!empty($_GET[$param])) { $selectedTags[] = trim($_GET[$param]); } } $legacyMultiple = isset($_GET['multiple_names']) ? trim($_GET['multiple_names']) : ''; if ($legacyMultiple !== '' && empty($selectedTags)) { $selectedTags = array_filter(array_map('trim', explode(',', $legacyMultiple))); } $startDateInput = isset($_GET['start_date']) ? $_GET['start_date'] : ''; $endDateInput = isset($_GET['end_date']) ? $_GET['end_date'] : ''; $normalizeDateTime = function ($value) { if ($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'); } } return $value; }; $startDate = $normalizeDateTime($startDateInput); $endDate = $normalizeDateTime($endDateInput); $limit = isset($_GET['limit']) ? (int) $_GET['limit'] : 100; if ($limit <= 0) { $limit = 100; } $showTrend = isset($_GET['show_trend']); $timeInterval = isset($_GET['time_interval']) ? (int) $_GET['time_interval'] : 30; if ($timeInterval < 1) { $timeInterval = 1; } $results = []; $searchTitle = ''; $totalCount = 0; $tagNames = []; $organizedResults = []; $canSearch = false; switch ($searchType) { case 'single_name': $canSearch = $searchName !== ''; break; case 'multiple_names': $canSearch = !empty($selectedTags); break; } if ($canSearch) { try { $sql = " SELECT a.ID, a.Value, a.TimeStamp, COALESCE(n.name, CONCAT('ID_', a.ID)) AS name FROM dbo.archive a LEFT JOIN dbo.id_names n ON a.ID = n.idnumber WHERE 1 = 1 AND a.TimeStamp <> :invalid_timestamp "; $countSql = " SELECT COUNT(*) FROM dbo.archive a LEFT JOIN dbo.id_names n ON a.ID = n.idnumber WHERE 1 = 1 AND a.TimeStamp <> :invalid_timestamp "; $params = []; $params[':invalid_timestamp'] = '1899-12-31 18:00:00'; 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 = []; foreach ($selectedTags as $index => $tag) { $placeholder = ':name' . $index; $placeholders[] = $placeholder; $params[$placeholder] = $tag; } $inClause = implode(',', $placeholders); $sql .= " AND n.name IN ($inClause)"; $countSql .= " AND n.name IN ($inClause)"; $preview = array_slice($selectedTags, 0, 3); $searchTitle = 'Multiple tags (' . count($selectedTags) . '): ' . implode(', ', $preview); if (count($selectedTags) > 3) { $searchTitle .= '…'; } } break; } if ($startDate !== '') { $sql .= " AND a.TimeStamp >= :start_date"; $countSql .= " AND a.TimeStamp >= :start_date"; $params[':start_date'] = $startDate; } if ($endDate !== '') { $sql .= " AND a.TimeStamp <= :end_date"; $countSql .= " AND a.TimeStamp <= :end_date"; $params[':end_date'] = $endDate; } if ($timeInterval > 1) { if ($timeInterval >= 60) { $minutes = (int) ($timeInterval / 60); $sql .= " AND DATEPART(MINUTE, a.TimeStamp) % :minute_interval = 0 AND DATEPART(SECOND, a.TimeStamp) = 0 "; $countSql .= " AND DATEPART(MINUTE, a.TimeStamp) % :minute_interval = 0 AND DATEPART(SECOND, a.TimeStamp) = 0 "; $params[':minute_interval'] = $minutes; } else { $sql .= " AND DATEPART(SECOND, a.TimeStamp) % :time_interval = 0"; $countSql .= " AND DATEPART(SECOND, a.TimeStamp) % :time_interval = 0"; $params[':time_interval'] = $timeInterval; } } $countStmt = $pdo->prepare($countSql); foreach ($params as $key => $value) { if ($key === ':limit') { continue; } $countStmt->bindValue($key, $value); } $countStmt->execute(); $totalCount = (int) $countStmt->fetchColumn(); if ($showTrend) { $sql .= " ORDER BY a.TimeStamp ASC OFFSET 0 ROWS FETCH NEXT :limit ROWS ONLY "; } else { $sql .= " ORDER BY a.TimeStamp DESC OFFSET 0 ROWS FETCH NEXT :limit ROWS ONLY "; } $params[':limit'] = $limit; $stmt = $pdo->prepare($sql); foreach ($params as $key => $value) { if ($key === ':limit') { $stmt->bindValue($key, $value, PDO::PARAM_INT); } else { $stmt->bindValue($key, $value); } } $queryStartTime = microtime(true); $stmt->execute(); $results = $stmt->fetchAll(); $queryEndTime = microtime(true); $executionMs = (int) round(($queryEndTime - $queryStartTime) * 1000); // Log the query for NL2SQL training $userQueryDesc = buildUserQueryDescription( $searchType, $searchName, $selectedTags, $startDate, $endDate, $limit, $timeInterval ); $logParams = $params; unset($logParams[':invalid_timestamp']); // Remove internal filter param logControlsQuery( $userQueryDesc, $sql, $logParams, 'opcsearch', $executionMs, count($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, true)) { $tagNames[] = $tagName; } } if (!empty($organizedResults)) { if ($showTrend && $searchType === 'single_name') { ksort($organizedResults); } else { krsort($organizedResults); } } } catch (PDOException $e) { echo '
Query historian tags by name, filter by time range, and export results or trend charts.
| Timestamp | |
|---|---|
No historian data matched the filters provided. Adjust the tag selection or date range and try again.