140 lines
4.6 KiB
PHP
140 lines
4.6 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Rate Trend</title>
|
||
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script>
|
||
<style>
|
||
body { background:#1f2a30; color:#fff; font-family:Arial,sans-serif; margin:0; padding:20px; }
|
||
.trend-card { max-width:960px; margin:0 auto; background:#263445; border-radius:12px; padding:20px; box-shadow:0 10px 25px rgba(0,0,0,.35); }
|
||
canvas { width:100%; height:420px; }
|
||
.header { display:flex; align-items:center; justify-content:space-between; margin-bottom:12px; }
|
||
.status-dot { width:12px; height:12px; border-radius:50%; margin-right:6px; background:#27ae60; animation:pulse 2s infinite; }
|
||
@keyframes pulse { 0%{opacity:.4;} 50%{opacity:1;} 100%{opacity:.4;} }
|
||
.controls { display:flex; gap:12px; flex-wrap:wrap; }
|
||
button { background:#3498db; border:none; color:#fff; padding:8px 16px; border-radius:6px; cursor:pointer; font-weight:600; }
|
||
button:hover { background:#2980b9; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="trend-card">
|
||
<div class="header">
|
||
<h2>West Mills – Rate</h2>
|
||
<div style="display:flex; align-items:center;">
|
||
<span class="status-dot"></span>
|
||
<small id="lastTimestamp">Waiting…</small>
|
||
</div>
|
||
</div>
|
||
<div class="controls">
|
||
<button id="resetZoom">Reset Zoom</button>
|
||
<span>Sampling every 1 s</span>
|
||
</div>
|
||
<canvas id="rateChart"></canvas>
|
||
</div>
|
||
|
||
<script>
|
||
const url = "http://192.168.0.10:57080/read?item=192.168.0.10->opc.tcp://192.168.0.10:49320/->ns=2;s=WEST MILLS.MILL CONTROL.RATE";
|
||
const pollMs = 1000;
|
||
const maxPoints = 1800;
|
||
|
||
Chart.register(ChartZoom);
|
||
|
||
const ctx = document.getElementById('rateChart').getContext('2d');
|
||
const rateChart = new Chart(ctx, {
|
||
type: 'line',
|
||
data: {
|
||
labels: [],
|
||
datasets: [{
|
||
label: 'Rate (tph)',
|
||
data: [],
|
||
spanGaps: true,
|
||
tension: 0.25,
|
||
borderColor: '#27ae60',
|
||
backgroundColor: 'rgba(39,174,96,0.12)',
|
||
borderWidth: 2,
|
||
pointRadius: 0
|
||
}]
|
||
},
|
||
options: {
|
||
responsive: true,
|
||
maintainAspectRatio: false,
|
||
scales: {
|
||
x: {
|
||
type: 'time',
|
||
ticks: { color:'#bdc3c7' },
|
||
grid: { color:'rgba(255,255,255,0.08)' }
|
||
},
|
||
y: {
|
||
beginAtZero: true,
|
||
ticks: { color:'#bdc3c7' },
|
||
grid: { color:'rgba(255,255,255,0.08)' }
|
||
}
|
||
},
|
||
plugins: {
|
||
legend: { labels:{ color:'#ecf0f1' } },
|
||
zoom: {
|
||
pan: {
|
||
enabled: true,
|
||
mode: 'x'
|
||
},
|
||
zoom: {
|
||
wheel: { enabled: true, modifierKey: 'shift' },
|
||
pinch: { enabled: true },
|
||
mode: 'x'
|
||
}
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
$('#resetZoom').on('click', () => {
|
||
rateChart.resetZoom();
|
||
});
|
||
|
||
setInterval(() => {
|
||
$.ajax({
|
||
type: "GET",
|
||
contentType: "application/json; charset=utf-8",
|
||
url,
|
||
dataType: "json",
|
||
cache: false,
|
||
success: function (response) {
|
||
if (!response?.data?.length) { return; }
|
||
const item = response.data[0];
|
||
const ts = new Date(item.SourceTimestamp);
|
||
const value = parseFloat(item.Value);
|
||
|
||
rateChart.data.labels.push(ts);
|
||
rateChart.data.datasets[0].data.push(value);
|
||
|
||
if (rateChart.data.labels.length > maxPoints) {
|
||
rateChart.data.labels.shift();
|
||
rateChart.data.datasets[0].data.shift();
|
||
}
|
||
|
||
rateChart.update('none');
|
||
$('#lastTimestamp').text(`Last update: ${ts.toLocaleString()} (${value.toFixed(2)} tph)`);
|
||
},
|
||
error: function () {
|
||
$('#lastTimestamp').text('Connection error');
|
||
}
|
||
});
|
||
}, pollMs);
|
||
|
||
<?php
|
||
$ch = curl_init("http://192.168.0.10:57080/read?item=...");
|
||
curl_setopt_array($ch, [
|
||
CURLOPT_NOBODY => true, // HEAD request
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_TIMEOUT => 5,
|
||
]);
|
||
curl_exec($ch);
|
||
$headers = curl_getinfo($ch);
|
||
curl_close($ch);
|
||
?>
|
||
</script>
|
||
</body>
|
||
</html>
|