plc_control/web/js/chart.js

98 lines
2.7 KiB
JavaScript

import { apiFetch } from "./api.js";
import { dom } from "./dom.js";
import { state } from "./state.js";
function normalizeChartItem(item) {
return {
timestamp: item?.timestamp || "",
valueNumber: typeof item?.value_number === "number" ? item.value_number : null,
valueText: item?.value_text || "",
};
}
export async function openChart(pointId, pointName) {
state.chartPointId = pointId;
state.chartPointName = pointName || "Point";
dom.chartTitle.textContent = `${state.chartPointName} Chart`;
const items = await apiFetch(`/api/point/${pointId}/history?limit=120`);
state.chartData = (items || [])
.map(normalizeChartItem)
.filter((item) => item.valueNumber !== null);
renderChart();
}
export function appendChartPoint(item) {
if (!state.chartPointId) {
return;
}
const normalized = normalizeChartItem(item);
if (normalized.valueNumber === null) {
return;
}
const last = state.chartData[state.chartData.length - 1];
if (
last &&
last.timestamp === normalized.timestamp &&
last.valueText === normalized.valueText &&
last.valueNumber === normalized.valueNumber
) {
return;
}
state.chartData.push(normalized);
if (state.chartData.length > 120) {
state.chartData = state.chartData.slice(-120);
}
renderChart();
}
export function renderChart() {
const ctx = dom.chartCanvas.getContext("2d");
const width = dom.chartCanvas.width;
const height = dom.chartCanvas.height;
ctx.clearRect(0, 0, width, height);
if (!state.chartData.length) {
ctx.fillStyle = "#94a3b8";
ctx.font = "14px Segoe UI";
ctx.fillText("Click a point row to view its chart", 24, 40);
dom.chartSummary.textContent = "Click a point row to view its chart";
return;
}
const values = state.chartData.map((item) => item.valueNumber);
let min = Math.min(...values);
let max = Math.max(...values);
if (min === max) {
min -= 1;
max += 1;
}
const padding = { top: 20, right: 20, bottom: 36, left: 52 };
const plotWidth = width - padding.left - padding.right;
const plotHeight = height - padding.top - padding.bottom;
ctx.strokeStyle = "#2563eb";
ctx.lineWidth = 2;
ctx.beginPath();
state.chartData.forEach((item, index) => {
const x = padding.left + (plotWidth * index) / Math.max(1, state.chartData.length - 1);
const y = padding.top + ((max - item.valueNumber) / (max - min)) * plotHeight;
if (index === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
});
ctx.stroke();
const latest = state.chartData[state.chartData.length - 1];
dom.chartSummary.textContent = `Latest ${state.chartData.length} points, current value ${latest.valueText || latest.valueNumber}`;
}