127 lines
4.8 KiB
TypeScript
127 lines
4.8 KiB
TypeScript
import { useState, useMemo } from 'react';
|
|
import Plot from 'react-plotly.js';
|
|
import { Node, DataWindow } from '../types';
|
|
|
|
interface SidebarProps {
|
|
selectedNode: Node | null;
|
|
dataWindow: DataWindow | null;
|
|
loading: boolean;
|
|
sampleRate: number;
|
|
}
|
|
|
|
type ViewMode = 'waveform' | 'rms';
|
|
|
|
function Sidebar({ selectedNode, dataWindow, loading, sampleRate }: SidebarProps) {
|
|
const [viewMode, setViewMode] = useState<ViewMode>('waveform');
|
|
const [showRaw, setShowRaw] = useState(false);
|
|
|
|
const samples = dataWindow?.samples || [];
|
|
const startTs = dataWindow?.startTimestamp || 0;
|
|
|
|
// Calcul RMS glissant
|
|
const rmsData = useMemo(() => {
|
|
if (samples.length === 0) return [];
|
|
const windowSize = 20;
|
|
const result = [];
|
|
for (let i = 0; i < samples.length; i++) {
|
|
const start = Math.max(0, i - windowSize);
|
|
const window = samples.slice(start, i + 1);
|
|
const rms = Math.sqrt(window.reduce((a, b) => a + b*b, 0) / window.length);
|
|
result.push(rms);
|
|
}
|
|
return result;
|
|
}, [samples]);
|
|
|
|
// Formatter pour le X (DD hh:mm:ss)
|
|
const formatXAxis = (ts: number) => {
|
|
const d = new Date(ts * 1000);
|
|
const day = d.getDate().toString().padStart(2, '0');
|
|
const h = d.getHours().toString().padStart(2, '0');
|
|
const m = d.getMinutes().toString().padStart(2, '0');
|
|
const s = d.getSeconds().toString().padStart(2, '0');
|
|
return `${day} ${h}:${m}:${s}`;
|
|
};
|
|
|
|
const xValues = useMemo(() => {
|
|
return samples.map((_, i) => formatXAxis(startTs + i / sampleRate));
|
|
}, [samples, startTs, sampleRate]);
|
|
|
|
const plotData: any[] = [];
|
|
if (samples.length > 0) {
|
|
if (viewMode === 'waveform') {
|
|
plotData.push({
|
|
x: xValues,
|
|
y: samples,
|
|
type: 'scatter',
|
|
mode: 'lines',
|
|
name: 'ADC Brute',
|
|
line: { color: '#4ade80', width: 1.5 }
|
|
});
|
|
} else {
|
|
plotData.push({
|
|
x: xValues,
|
|
y: rmsData,
|
|
type: 'scatter',
|
|
mode: 'lines',
|
|
name: 'RMS',
|
|
line: { color: '#fbbf24', width: 2 }
|
|
});
|
|
}
|
|
}
|
|
|
|
return (
|
|
<aside className="sidebar" style={{ width: '450px', background: '#1e293b', borderLeft: '1px solid #334155', display: 'flex', flexDirection: 'column', height: '100%' }}>
|
|
<div style={{ padding: '20px', borderBottom: '1px solid #334155' }}>
|
|
<h2 style={{ margin: 0, color: '#f8fafc' }}>Analyse Node</h2>
|
|
</div>
|
|
|
|
<div style={{ flex: 1, overflowY: 'auto', padding: '20px' }}>
|
|
{selectedNode ? (
|
|
<>
|
|
<div style={{ marginBottom: '20px', background: '#0f172a', padding: '15px', borderRadius: '8px' }}>
|
|
<h3 style={{ margin: '0 0 10px 0', color: '#38bdf8' }}>Node b{selectedNode.id}</h3>
|
|
<p style={{ margin: '5px 0', fontSize: '0.9rem' }}>E: {selectedNode.position?.easting.toFixed(0)} N: {selectedNode.position?.northing.toFixed(0)}</p>
|
|
</div>
|
|
|
|
<div style={{ display: 'flex', gap: '5px', marginBottom: '15px' }}>
|
|
<button onClick={() => setViewMode('waveform')} style={{ flex: 1, padding: '8px', background: viewMode === 'waveform' ? '#3b82f6' : '#334155', border: 'none', color: '#fff', borderRadius: '4px', cursor: 'pointer' }}>Waveform (Brute)</button>
|
|
<button onClick={() => setViewMode('rms')} style={{ flex: 1, padding: '8px', background: viewMode === 'rms' ? '#3b82f6' : '#334155', border: 'none', color: '#fff', borderRadius: '4px', cursor: 'pointer' }}>RMS</button>
|
|
</div>
|
|
|
|
{loading ? (
|
|
<div style={{ textAlign: 'center', padding: '40px', color: '#94a3b8' }}>Chargement…</div>
|
|
) : samples.length > 0 ? (
|
|
<>
|
|
<div style={{ background: '#000', borderRadius: '8px', overflow: 'hidden', height: '300px' }}>
|
|
<Plot
|
|
data={plotData}
|
|
layout={{
|
|
autosize: true,
|
|
height: 300,
|
|
margin: { t: 10, b: 60, l: 50, r: 10 },
|
|
paper_bgcolor: 'rgba(0,0,0,0)',
|
|
plot_bgcolor: 'rgba(0,0,0,0)',
|
|
font: { color: '#94a3b8', size: 10 },
|
|
xaxis: {
|
|
gridcolor: '#1e293b',
|
|
zerolinecolor: '#334155',
|
|
tickangle: -45,
|
|
nticks: 5
|
|
},
|
|
yaxis: { gridcolor: '#1e293b', zerolinecolor: '#334155', title: 'Amplitude' }
|
|
}}
|
|
config={{ responsive: true, displayModeBar: true }}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</div>
|
|
{/* Stats... */}
|
|
</>
|
|
) : null}
|
|
</>
|
|
) : null}
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|
|
|
|
export default Sidebar; |