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'])) { while (ob_get_level()) { ob_end_clean(); } ob_start(); header('Content-Type: application/json'); try { switch ($_POST['action']) { case 'get_schema': // Get available tags and their data types $tagsStmt = $pdo->prepare("SELECT DISTINCT name FROM id_names WHERE name IS NOT NULL AND name != '' ORDER BY name"); $tagsStmt->execute(); $tags = $tagsStmt->fetchAll(PDO::FETCH_COLUMN); // Get sample data to determine data types and ranges $schema = []; foreach (array_slice($tags, 0, 50) as $tag) { // Limit to first 50 tags for performance $sampleStmt = $pdo->prepare(" SELECT MIN(h.Value) as min_value, MAX(h.Value) as max_value, AVG(h.Value) as avg_value, COUNT(*) as record_count, MIN(h.TimeStamp) as earliest_date, MAX(h.TimeStamp) as latest_date FROM historicaldata h INNER JOIN id_names n ON h.ID = n.idnumber WHERE n.name = ? AND h.Value IS NOT NULL AND h.TimeStamp >= DATE_SUB(NOW(), INTERVAL 30 DAY) "); $sampleStmt->execute([$tag]); $stats = $sampleStmt->fetch(PDO::FETCH_ASSOC); if ($stats && $stats['record_count'] > 0) { $schema[] = [ 'name' => $tag, 'type' => 'numeric', 'min_value' => (float)$stats['min_value'], 'max_value' => (float)$stats['max_value'], 'avg_value' => (float)$stats['avg_value'], 'record_count' => (int)$stats['record_count'], 'earliest_date' => $stats['earliest_date'], 'latest_date' => $stats['latest_date'] ]; } } echo json_encode([ 'success' => true, 'schema' => $schema, 'total_tags' => count($tags) ]); break; case 'execute_query': $queryConfig = json_decode($_POST['query_config'] ?? '{}', true); if (empty($queryConfig) || !isset($queryConfig['conditions'])) { throw new Exception('Invalid query configuration'); } // Build SQL query from configuration $sql = $this->buildSQLFromConfig($queryConfig, $pdo); // Execute query with limit for safety $limit = min((int)($queryConfig['limit'] ?? 1000), 10000); $sql .= " LIMIT " . $limit; $stmt = $pdo->prepare($sql['query']); $stmt->execute($sql['params']); $results = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode([ 'success' => true, 'data' => $results, 'row_count' => count($results), 'sql_query' => $sql['query'], 'parameters' => $sql['params'], 'execution_time' => microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'] ]); break; case 'save_query': $queryName = $_POST['query_name'] ?? ''; $queryConfig = $_POST['query_config'] ?? ''; $userId = $_SESSION['user_id'] ?? 'anonymous'; if (empty($queryName) || empty($queryConfig)) { throw new Exception('Query name and configuration required'); } // Create saved queries table if it doesn't exist $pdo->exec("CREATE TABLE IF NOT EXISTS saved_queries ( id INT AUTO_INCREMENT PRIMARY KEY, user_id VARCHAR(50), query_name VARCHAR(100), query_config TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )"); $stmt = $pdo->prepare("INSERT INTO saved_queries (user_id, query_name, query_config) VALUES (?, ?, ?)"); $stmt->execute([$userId, $queryName, $queryConfig]); echo json_encode([ 'success' => true, 'query_id' => $pdo->lastInsertId(), 'message' => 'Query saved successfully' ]); break; case 'load_saved_queries': $userId = $_SESSION['user_id'] ?? 'anonymous'; $stmt = $pdo->prepare("SELECT id, query_name, query_config, created_at FROM saved_queries WHERE user_id = ? ORDER BY created_at DESC"); $stmt->execute([$userId]); $queries = $stmt->fetchAll(PDO::FETCH_ASSOC); echo json_encode([ 'success' => true, 'saved_queries' => $queries ]); break; case 'get_field_values': $fieldName = $_POST['field_name'] ?? ''; if (empty($fieldName)) { throw new Exception('Field name required'); } // Get distinct values for the field (limited for performance) $stmt = $pdo->prepare(" SELECT DISTINCT h.Value FROM historicaldata h INNER JOIN id_names n ON h.ID = n.idnumber WHERE n.name = ? AND h.Value IS NOT NULL AND h.TimeStamp >= DATE_SUB(NOW(), INTERVAL 7 DAY) ORDER BY h.Value LIMIT 100 "); $stmt->execute([$fieldName]); $values = $stmt->fetchAll(PDO::FETCH_COLUMN); echo json_encode([ 'success' => true, 'field_name' => $fieldName, 'values' => array_map('floatval', $values) ]); 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; } // Helper function to build SQL from query configuration function buildSQLFromConfig($config, $pdo) { $selectedTags = $config['selected_tags'] ?? []; $conditions = $config['conditions'] ?? []; $orderBy = $config['order_by'] ?? []; $groupBy = $config['group_by'] ?? []; $aggregations = $config['aggregations'] ?? []; if (empty($selectedTags)) { throw new Exception('No tags selected'); } // Build SELECT clause $selectFields = []; $selectFields[] = "h.TimeStamp"; if (!empty($aggregations)) { // Aggregation query foreach ($selectedTags as $tag) { foreach ($aggregations as $agg) { $selectFields[] = "{$agg['function']}(CASE WHEN n.name = '{$tag}' THEN h.Value END) as {$tag}_{$agg['function']}"; } } } else { // Regular query $selectFields[] = "n.name as tag_name"; $selectFields[] = "h.Value"; } $sql = "SELECT " . implode(', ', $selectFields) . " FROM historicaldata h INNER JOIN id_names n ON h.ID = n.idnumber"; // Build WHERE clause $whereConditions = []; $params = []; // Tag filter $tagPlaceholders = str_repeat('?,', count($selectedTags) - 1) . '?'; $whereConditions[] = "n.name IN ($tagPlaceholders)"; $params = array_merge($params, $selectedTags); // Custom conditions foreach ($conditions as $condition) { if (empty($condition['field']) || empty($condition['operator'])) continue; $field = $condition['field']; $operator = $condition['operator']; $value = $condition['value']; $logicalOp = $condition['logical'] ?? 'AND'; $conditionSql = ''; switch ($operator) { case 'equals': if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp = ?"; } else { $conditionSql = "(n.name = ? AND h.Value = ?)"; $params[] = $field; } $params[] = $value; break; case 'not_equals': if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp != ?"; } else { $conditionSql = "(n.name = ? AND h.Value != ?)"; $params[] = $field; } $params[] = $value; break; case 'greater_than': if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp > ?"; } else { $conditionSql = "(n.name = ? AND h.Value > ?)"; $params[] = $field; } $params[] = $value; break; case 'less_than': if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp < ?"; } else { $conditionSql = "(n.name = ? AND h.Value < ?)"; $params[] = $field; } $params[] = $value; break; case 'between': $values = explode(',', $value); if (count($values) === 2) { if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp BETWEEN ? AND ?"; } else { $conditionSql = "(n.name = ? AND h.Value BETWEEN ? AND ?)"; $params[] = $field; } $params[] = trim($values[0]); $params[] = trim($values[1]); } break; case 'in': $values = explode(',', $value); $valuePlaceholders = str_repeat('?,', count($values) - 1) . '?'; if ($field === 'TimeStamp') { $conditionSql = "h.TimeStamp IN ($valuePlaceholders)"; } else { $conditionSql = "(n.name = ? AND h.Value IN ($valuePlaceholders))"; $params[] = $field; } foreach ($values as $val) { $params[] = trim($val); } break; } if ($conditionSql) { if (!empty($whereConditions) && count($whereConditions) > 1) { $whereConditions[] = $logicalOp . ' ' . $conditionSql; } else { $whereConditions[] = $conditionSql; } } } if (!empty($whereConditions)) { $sql .= " WHERE " . implode(' ', $whereConditions); } // Add GROUP BY if (!empty($groupBy)) { $sql .= " GROUP BY " . implode(', ', $groupBy); } // Add ORDER BY if (!empty($orderBy)) { $orderClauses = []; foreach ($orderBy as $order) { $orderClauses[] = $order['field'] . ' ' . ($order['direction'] ?? 'ASC'); } $sql .= " ORDER BY " . implode(', ', $orderClauses); } else { $sql .= " ORDER BY h.TimeStamp DESC"; } return ['query' => $sql, 'params' => $params]; } ?>
Visual Data Filtering & Analysis Tool