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'])) { // 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 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_pivot_data': $selectedTagsRaw = $_POST['tags'] ?? '[]'; if (is_string($selectedTagsRaw)) { $selectedTags = json_decode($selectedTagsRaw, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new Exception('Invalid tags JSON: ' . json_last_error_msg()); } } else { $selectedTags = $selectedTagsRaw; } if (empty($selectedTags) || !is_array($selectedTags)) { throw new Exception('No tags selected or invalid tags format'); } $startDateInput = $_POST['start_date'] ?? ''; $endDateInput = $_POST['end_date'] ?? ''; $groupBy = $_POST['group_by'] ?? 'hour'; $aggregation = $_POST['aggregation'] ?? 'avg'; $limit = (int) ($_POST['limit'] ?? 1000); if ($limit <= 0 || $limit > 50000) { $limit = 1000; } $normalizeDateTime = static function ($value) { if ($value === '' || $value === null) { return ''; } $patterns = ['Y-m-d\TH:i:s', 'Y-m-d\TH:i']; foreach ($patterns as $pattern) { $dt = DateTimeImmutable::createFromFormat($pattern, $value); if ($dt instanceof DateTimeImmutable) { return $dt->format('Y-m-d H:i:s'); } } $dt = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value); if ($dt instanceof DateTimeImmutable) { return $dt->format('Y-m-d H:i:s'); } return $value; }; $startDate = $normalizeDateTime($startDateInput); $endDate = $normalizeDateTime($endDateInput); $aggFunction = strtoupper($aggregation); if (!in_array($aggFunction, ['AVG', 'SUM', 'MIN', 'MAX', 'COUNT'], true)) { $aggFunction = 'AVG'; } switch ($groupBy) { case 'minute': $timeGroupExpr = 'CONVERT(varchar(19), DATEADD(MINUTE, DATEDIFF(MINUTE, 0, h.TimeStamp), 0), 120)'; break; case 'day': $timeGroupExpr = 'CONVERT(varchar(19), DATEADD(DAY, DATEDIFF(DAY, 0, h.TimeStamp), 0), 120)'; break; case 'month': $timeGroupExpr = 'CONVERT(varchar(19), CAST(DATEFROMPARTS(YEAR(h.TimeStamp), MONTH(h.TimeStamp), 1) AS datetime), 120)'; break; case 'hour': default: $timeGroupExpr = 'CONVERT(varchar(19), DATEADD(HOUR, DATEDIFF(HOUR, 0, h.TimeStamp), 0), 120)'; break; } if ($aggFunction === 'COUNT') { $valueExpression = 'COUNT(*)'; } else { $valueExpression = $aggFunction . '(CAST(h.Value AS float))'; } $placeholders = implode(',', array_fill(0, count($selectedTags), '?')); $params = $selectedTags; $sql = " SELECT TOP {$limit} {$timeGroupExpr} AS time_group, n.name AS tag_name, {$valueExpression} AS value, COUNT(*) AS data_points FROM dbo.historicaldata AS h INNER JOIN dbo.id_names AS n ON h.ID = n.idnumber WHERE n.name IN ({$placeholders}) "; if ($startDate !== '') { $sql .= ' AND h.TimeStamp >= ?'; $params[] = $startDate; } if ($endDate !== '') { $sql .= ' AND h.TimeStamp <= ?'; $params[] = $endDate; } $sql .= " GROUP BY {$timeGroupExpr}, n.name ORDER BY time_group ASC, n.name ASC "; 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); $pivotData = []; $timeGroups = []; $actualTagNames = []; foreach ($results as $row) { $timeGroup = $row['time_group']; $tagName = $row['tag_name']; $value = (float) $row['value']; if (!in_array($timeGroup, $timeGroups, true)) { $timeGroups[] = $timeGroup; } if (!in_array($tagName, $actualTagNames, true)) { $actualTagNames[] = $tagName; } if (!isset($pivotData[$timeGroup])) { $pivotData[$timeGroup] = ['time_group' => $timeGroup]; } $pivotData[$timeGroup][$tagName] = $value; } foreach ($pivotData as &$row) { foreach ($actualTagNames as $tagName) { if (!array_key_exists($tagName, $row)) { $row[$tagName] = null; } } } unset($row); echo json_encode([ 'success' => true, 'data' => array_values($pivotData), 'timeGroups' => $timeGroups, 'tagNames' => $actualTagNames, '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 } ?> LASUCA Controls - Pivot Chart Analysis

LASUCA Controls - Pivot Chart Analysis

Dynamic Data Analysis & Visualization

Analysis Configuration

📊 Tag Selection

Loading tags...

📅 Time Range

⚙️ Analysis Settings