From c38ec08c48f47da4fb0627b399a1708547f41497 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sat, 22 Nov 2025 18:25:54 +0000 Subject: [PATCH] 1.00 --- api/get_radar_data.php | 21 +++++ assets/css/custom.css | 34 ++++++++ assets/js/main.js | 174 ++++++++++++++++++++++++++++++++++++++++ connect.php | 72 +++++++++++++++++ dashboard.php | 75 +++++++++++++++++ includes/thingspeak.php | 18 +++++ index.php | 167 +++++--------------------------------- partials/footer.php | 14 ++++ partials/header.php | 52 ++++++++++++ 9 files changed, 481 insertions(+), 146 deletions(-) create mode 100644 api/get_radar_data.php create mode 100644 assets/css/custom.css create mode 100644 assets/js/main.js create mode 100644 connect.php create mode 100644 dashboard.php create mode 100644 includes/thingspeak.php create mode 100644 partials/footer.php create mode 100644 partials/header.php diff --git a/api/get_radar_data.php b/api/get_radar_data.php new file mode 100644 index 0000000..f579372 --- /dev/null +++ b/api/get_radar_data.php @@ -0,0 +1,21 @@ + 'Not connected. Please set Channel ID and API Key.']); + exit; +} + +$data = fetch_thingspeak_data($channel_id, $api_key, 50); // Fetch more points for better clustering + +if ($data && !empty($data['feeds'])) { + echo json_encode($data['feeds']); +} else { + echo json_encode(['error' => 'Failed to fetch data from ThingSpeak.']); +} diff --git a/assets/css/custom.css b/assets/css/custom.css new file mode 100644 index 0000000..0286675 --- /dev/null +++ b/assets/css/custom.css @@ -0,0 +1,34 @@ + +body { + font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + background-color: #F8F9FA; +} + +.navbar-brand { + font-weight: 600; +} + +.card { + border-radius: 0.5rem; + box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); +} + +.form-control:focus { + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); +} + +.btn-primary { + background-image: linear-gradient(to right, #0D6EFD, #0D47A1); + border: none; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..e6254c4 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,174 @@ +document.addEventListener('DOMContentLoaded', function() { + const radarCanvas = document.getElementById('radarCanvas'); + if (radarCanvas) { + updateRadarData(); + setInterval(updateRadarData, 5000); // Update every 5 seconds + } +}); + +function updateRadarData() { + fetch('api/get_radar_data.php') + .then(response => response.json()) + .then(data => { + if (data && !data.error) { + const feeds = data; + checkProximity(feeds, 0.5); + drawRadar(feeds); + updateObjectsTable(feeds); + } else { + console.error('Error fetching radar data:', data ? data.error : 'No data received'); + } + }) + .catch(error => { + console.error('Failed to fetch or parse radar data:', error); + }); +} + +function toCartesian(angle, distance, canvas) { + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const maxCanvasDistance = Math.min(centerX, centerY) * 0.9; + const MAX_REAL_WORLD_METERS = 5; + + const distanceInMeters = distance / 100; + const normalizedDistance = (distanceInMeters / MAX_REAL_WORLD_METERS) * maxCanvasDistance; + const angleRad = (angle - 90) * (Math.PI / 180); + const x = centerX + normalizedDistance * Math.cos(angleRad); + const y = centerY + normalizedDistance * Math.sin(angleRad); + return { x, y }; +} + +function updateObjectsTable(feeds) { + const tableBody = document.getElementById('objectsTableBody'); + if (!tableBody) return; + + tableBody.innerHTML = ''; + + feeds.forEach((feed, index) => { + const row = document.createElement('tr'); + row.style.animation = `fadeIn 0.5s ease-in-out ${index * 0.1}s both`; + + const idCell = document.createElement('td'); + idCell.textContent = feed.entry_id; + row.appendChild(idCell); + + const distanceCell = document.createElement('td'); + const distanceInMeters = parseFloat(feed.field2) / 100; + distanceCell.textContent = distanceInMeters.toFixed(2); + row.appendChild(distanceCell); + + const angleCell = document.createElement('td'); + angleCell.textContent = parseFloat(feed.field1).toFixed(2); + row.appendChild(angleCell); + + const timeCell = document.createElement('td'); + timeCell.textContent = new Date(feed.created_at).toLocaleTimeString(); + row.appendChild(timeCell); + + tableBody.appendChild(row); + }); +} + +function checkProximity(feeds, distanceThreshold) { + const alertElement = document.getElementById('proximityAlert'); + const distanceElement = document.getElementById('closestDistance'); + if (!alertElement || !distanceElement) return; + + let minDistance = Infinity; + let isClose = false; + + feeds.forEach(feed => { + const distanceInMeters = parseFloat(feed.field2) / 100; + if (distanceInMeters < minDistance) { + minDistance = distanceInMeters; + } + if (distanceInMeters < distanceThreshold) { + isClose = true; + } + }); + + if (isClose) { + distanceElement.textContent = minDistance.toFixed(2); + alertElement.classList.remove('d-none'); + } else { + alertElement.classList.add('d-none'); + } +} + +function drawRadar(feeds) { + const canvas = document.getElementById('radarCanvas'); + if(!canvas) return; + const ctx = canvas.getContext('2d'); + const width = canvas.width; + const height = canvas.height; + const centerX = width / 2; + const centerY = height / 2; + const maxDistance = Math.min(centerX, centerY) * 0.9; + + ctx.fillStyle = '#0b1220'; + ctx.fillRect(0, 0, width, height); + ctx.strokeStyle = 'rgba(0, 255, 213, 0.3)'; + ctx.lineWidth = 1; + + for (let i = 1; i <= 4; i++) { + ctx.beginPath(); + ctx.arc(centerX, centerY, maxDistance * (i / 4), 0, 2 * Math.PI); + ctx.stroke(); + } + ctx.beginPath(); + ctx.moveTo(centerX, 0); + ctx.lineTo(centerX, height); + ctx.moveTo(0, centerY); + ctx.lineTo(width, centerY); + ctx.stroke(); + + feeds.forEach((feed, index) => { + const angle = parseFloat(feed.field1); + const distance = parseFloat(feed.field2); + + if (!isNaN(angle) && !isNaN(distance)) { + const { x, y } = toCartesian(angle, distance, canvas); + + // Animation + let radius = 0; + let alpha = 0; + const targetRadius = 5; + const animationDuration = 500; + const startTime = Date.now(); + + function animateDot() { + const elapsedTime = Date.now() - startTime; + const progress = Math.min(elapsedTime / animationDuration, 1); + + radius = targetRadius * progress; + alpha = progress; + + ctx.fillStyle = `rgba(33, 150, 243, ${alpha})`; + ctx.beginPath(); + ctx.arc(x, y, radius, 0, 2 * Math.PI); + ctx.fill(); + + if (progress < 1) { + requestAnimationFrame(animateDot); + } + } + + setTimeout(animateDot, index * 100); + } + }); +} + +(function () { + 'use strict' + var forms = document.querySelectorAll('.needs-validation') + Array.prototype.slice.call(forms) + .forEach(function (form) { + form.addEventListener('submit', function (event) { + if (!form.checkValidity()) { + event.preventDefault() + event.stopPropagation() + } + form.classList.add('was-validated') + }, false) + }) +})(); \ No newline at end of file diff --git a/connect.php b/connect.php new file mode 100644 index 0000000..9c003a0 --- /dev/null +++ b/connect.php @@ -0,0 +1,72 @@ +Success! Your settings have been saved. You can now proceed to the dashboard.'; + $message_type = 'success'; + } else { + $message = 'Error! Please provide a valid Channel ID and Read API Key.'; + $message_type = 'danger'; + } +} + +include 'partials/header.php'; +?> + +
+
+
+
+
+

Connect IoT Channel

+ + + + + +
+
+ + +
+ Please enter your ThingSpeak Channel ID. +
+
+
+ + +
+ Please enter your Read API Key. +
+
+
+ +
+
+
+
+
+
+
+ + diff --git a/dashboard.php b/dashboard.php new file mode 100644 index 0000000..a0a0824 --- /dev/null +++ b/dashboard.php @@ -0,0 +1,75 @@ + + +
+
+

Dashboard

+
+

This is where the synchronized polar and Cartesian plots will be displayed.

+ + +
+
+
+
+ Radar View +
+
+ +
+
+
+
+
+
+ Detected Objects +
+
+ + + + + + + + + + + + +
IDDistance (m)Angle (°)Timestamp
+
+
+
+
+ +
+ + Raw ThingSpeak Data + +
+
Fetching data...
+
+
+ + + +
+
+
+ + diff --git a/includes/thingspeak.php b/includes/thingspeak.php new file mode 100644 index 0000000..9e14985 --- /dev/null +++ b/includes/thingspeak.php @@ -0,0 +1,18 @@ + \ No newline at end of file diff --git a/index.php b/index.php index 7205f3d..3481159 100644 --- a/index.php +++ b/index.php @@ -1,150 +1,25 @@ - - - - - - New Style - - - - - - - - - - - - - - - - - - - - - -
-
-

Analyzing your requirements and generating your website…

-
- Loading… -
-

AI is collecting your requirements and applying the first changes.

-

This page will update automatically as the plan is implemented.

-

Runtime: PHP — UTC

+ +
+
+
+ Bootstrap Themes +
+
+

Monitor Real-Time IoT Data with Ease

+

+ +
-
- - - + + + \ No newline at end of file diff --git a/partials/footer.php b/partials/footer.php new file mode 100644 index 0000000..1b65623 --- /dev/null +++ b/partials/footer.php @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/partials/header.php b/partials/header.php new file mode 100644 index 0000000..81d9a71 --- /dev/null +++ b/partials/header.php @@ -0,0 +1,52 @@ + + + + + + + <?php echo htmlspecialchars($page_title ?? $project_name); ?> + + + + + + + + + + + + + + + + + + +