@@ -15,16 +15,64 @@ import {
1515} from '@/components/ui/dialog' ;
1616import SignalGraphView from './signal-graph-full' ;
1717import ExportDialog from '@/components/ui/export-dialog' ;
18+ import { exportEEGData } from '@/lib/eeg-api' ;
19+
20+ interface SignalDataPoint {
21+ time : string ;
22+ signal1 : number ;
23+ signal2 : number ;
24+ signal3 : number ;
25+ signal4 : number ;
26+ }
27+
28+ function parseEEG ( csvContent : string ) : SignalDataPoint [ ] {
29+ try {
30+ const lines = csvContent . trim ( ) . split ( '\n' ) ;
31+ if ( lines . length < 2 ) return [ ] ;
32+
33+ const header = lines [ 0 ] . split ( ',' ) . map ( ( h ) => h . trim ( ) . toLowerCase ( ) ) ;
34+ // find time column
35+ const timeColIdx = header . findIndex ( ( h ) => h . includes ( 'time' ) || h === 'timestamp' ) ;
36+ if ( timeColIdx == - 1 ) return [ ] ;
37+
38+ // find signal columns by exact channel number patterns
39+ const signalIndices = [ 0 , 1 , 2 , 3 ] . map ( ( chNum ) => {
40+ const patterns = [ `ch${ chNum } ` , `channel${ chNum } ` , `signal${ chNum + 1 } ` , `signal_${ chNum + 1 } ` ] ;
41+ return header . findIndex ( ( h ) => patterns . includes ( h ) ) ;
42+ } ) ;
43+
44+ if ( signalIndices . includes ( - 1 ) ) return [ ] ;
45+
46+ const data : SignalDataPoint [ ] = [ ] ;
47+ for ( let i = 1 ; i < lines . length ; i ++ ) {
48+ const cols = lines [ i ] . trim ( ) . split ( ',' ) . map ( ( c ) => c . trim ( ) ) ;
49+ if ( ! cols [ timeColIdx ] ) continue ;
50+
51+ const vals = signalIndices . map ( ( idx ) => parseFloat ( cols [ idx ] ) ) ;
52+ if ( vals . every ( ( v ) => ! isNaN ( v ) ) ) {
53+ data . push ( {
54+ time : cols [ timeColIdx ] ,
55+ signal1 : vals [ 0 ] ,
56+ signal2 : vals [ 1 ] ,
57+ signal3 : vals [ 2 ] ,
58+ signal4 : vals [ 3 ] ,
59+ } ) ;
60+ }
61+ }
62+ return data ;
63+ } catch ( err ) {
64+ console . error ( 'Failed to parse EEG CSV:' , err ) ;
65+ return [ ] ;
66+ }
67+ }
1868
1969export default function SignalGraphNode ( { id } : { id ?: string } ) {
20- const { dataStreaming } = useGlobalContext ( ) ;
70+ const { dataStreaming, activeSessionId } = useGlobalContext ( ) ;
2171 const { renderData } = useNodeData ( 500 , 10 ) ;
22-
23- const processedData = renderData ;
2472 const reactFlowInstance = useReactFlow ( ) ;
2573 const [ isConnected , setIsConnected ] = React . useState ( false ) ;
26- const { activeSessionId } = useGlobalContext ( ) ;
2774 const [ isExportOpen , setIsExportOpen ] = useState ( false ) ;
75+ const [ seedData , setSeedData ] = React . useState < SignalDataPoint [ ] > ( [ ] ) ;
2876
2977 // Determine if this Chart View node has an upstream path from a Source
3078 const checkConnectionStatus = React . useCallback ( ( ) => {
@@ -66,6 +114,40 @@ export default function SignalGraphNode({ id }: { id?: string }) {
66114 } ;
67115 } , [ checkConnectionStatus ] ) ;
68116
117+ // Fetch EEG data on load
118+ React . useEffect ( ( ) => {
119+ if ( ! id || ! activeSessionId ) return ;
120+
121+ const nodes = reactFlowInstance . getNodes ( ) ;
122+ const currentNode = nodes . find ( ( n ) => n . id === id ) ;
123+ if ( ! currentNode ?. data ) return ;
124+
125+ const { timeframeStart, timeframeEnd } = currentNode . data as { timeframeStart ?: string ; timeframeEnd ?: string } ;
126+ if ( ! timeframeStart || ! timeframeEnd ) return ;
127+
128+ console . log ( 'restored timeframe:' , { timeframeStart, timeframeEnd } ) ;
129+ ( async ( ) => {
130+ try {
131+ const csvContent = await exportEEGData ( activeSessionId , { start_time : timeframeStart , end_time : timeframeEnd } ) ;
132+ setSeedData ( parseEEG ( csvContent ) ) ; // store as seed data
133+ console . log ( 'loaded saved data' ) ;
134+ } catch ( err ) {
135+ console . debug ( 'export failed' , err ) ;
136+ setSeedData ( [ ] ) ;
137+ }
138+ } ) ( ) ;
139+ } , [ id , activeSessionId , reactFlowInstance ] ) ;
140+
141+ // Save timeframe in node data
142+ const handleTimeframeChange = ( start : string , end : string ) => {
143+ if ( ! id ) return ;
144+ reactFlowInstance . setNodes ( ( prevNodes ) =>
145+ prevNodes . map ( ( n ) =>
146+ n . id === id ? { ...n , data : { ...n . data , timeframeStart : start , timeframeEnd : end } } : n
147+ )
148+ ) ;
149+ } ;
150+
69151 return (
70152 < Dialog >
71153 < Card className = "rounded-[30px] border-2 border-[#D3D3D3] shadow-none p-0 overflow-hidden bg-white h-[96px] w-[396px]" >
@@ -143,7 +225,10 @@ export default function SignalGraphNode({ id }: { id?: string }) {
143225 < DialogDescription > </ DialogDescription >
144226 </ DialogHeader >
145227 < div className = "w-[85vw] h-[90vh]" >
146- < SignalGraphView data = { isConnected ? processedData : [ ] } />
228+ < SignalGraphView
229+ data = { seedData . length > 0 ? seedData : ( isConnected ? renderData : [ ] ) }
230+ onTimeframeChange = { handleTimeframeChange }
231+ />
147232 </ div >
148233 </ DialogContent >
149234 </ Card >
0 commit comments