@@ -4,7 +4,9 @@ const JOB = 'jobs_info';
44const JOBNAME = 'job_name' ;
55const USER = 'user_name' ;
66let mode = 'historical' ; // 'realTime' or 'historical'
7- const timeFormat = d3 . timeFormat ( '%Y-%m-%dT%H:%M:%S-05:00' ) ;
7+ const timeFormat = d3 . timeFormat ( '%Y-%m-%dT%H:%M:%S-06:00' ) ;
8+ let sampleMinTime = null ;
9+ let sampleMaxTime = null ;
810// layout
911let Layout = {
1012 data : { } ,
@@ -114,26 +116,24 @@ function renderModeMenu() {
114116 // Handle time range changes
115117 d3 . select ( '#realTimeRange' )
116118 . on ( 'change' , function ( ) {
117- const selected = + this . value ;
118- const intervalSelect = d3 . select ( '#realTimeInterval' ) ;
119- const intervalBox = d3 . select ( '#intervalContainer' ) ;
120- const startEndBox = d3 . select ( '#startEndContainer' ) ;
121- const processBtn = d3 . select ( '#processBtn' ) ;
122-
123- if ( selected === - 1 ) {
124- intervalBox . style ( 'display' , 'none' ) ;
125- startEndBox . style ( 'display' , null ) ; // show time inputs
126- processBtn . style ( 'display' , null ) ; // show Process button
127- } else if ( selected === - 3 ) {
128- intervalBox . style ( 'display' , 'none' ) ;
129- startEndBox . style ( 'display' , 'none' ) ;
130- processBtn . style ( 'display' , 'none' ) ;
131- loadSampleData ( )
132- }
133- else {
134- intervalBox . style ( 'display' , null ) ; // show interval
135- startEndBox . style ( 'display' , 'none' ) ; // hide time inputs
136- processBtn . style ( 'display' , 'none' ) ; // hide button
119+ const selected = + this . value ;
120+ const intervalSelect = d3 . select ( '#realTimeInterval' ) ;
121+ const intervalBox = d3 . select ( '#intervalContainer' ) ;
122+ const startEndBox = d3 . select ( '#startEndContainer' ) ;
123+ const processBtn = d3 . select ( '#processBtn' ) ;
124+ if ( selected === - 1 ) {
125+ intervalBox . style ( 'display' , 'none' ) ;
126+ startEndBox . style ( 'display' , null ) ;
127+ processBtn . style ( 'display' , null ) ;
128+ } else if ( selected === - 3 ) {
129+ intervalBox . style ( 'display' , 'none' ) ;
130+ startEndBox . style ( 'display' , null ) ;
131+ processBtn . style ( 'display' , null ) ;
132+ loadSampleData ( )
133+ } else {
134+ intervalBox . style ( 'display' , null ) ; // show interval
135+ startEndBox . style ( 'display' , 'none' ) ; // hide time inputs
136+ processBtn . style ( 'display' , 'none' ) ; // hide button
137137
138138 // Update interval options
139139 const intervalOptions = INTERVALS [ selected ] || [ ] ;
@@ -458,6 +458,7 @@ async function fetchDataAndProcess(Params, isRealTime = true) {
458458 cpu_power : Array ( len ) . fill ( ) . map ( ( ) => [ ] ) ,
459459 // gpu_mem: Array(len).fill().map(() => []),
460460 // gpu_usage: Array(len).fill().map(() => []),
461+ memory_usage : Array ( len ) . fill ( ) . map ( ( ) => [ ] ) ,
461462 temperature : Array ( len ) . fill ( ) . map ( ( ) => [ ] ) ,
462463 cpu_usage : Array ( len ) . fill ( ) . map ( ( ) => [ ] ) ,
463464 dram_usage : Array ( len ) . fill ( ) . map ( ( ) => [ ] ) ,
@@ -466,14 +467,14 @@ async function fetchDataAndProcess(Params, isRealTime = true) {
466467 jobName : [ ]
467468 } ;
468469 }
469-
470470 nodes_info [ node ] . cpus [ idx ] = entry . cores ?? [ ] ;
471471 nodes_info [ node ] . job_id [ idx ] = entry . jobs ?? [ ] ;
472472 nodes_info [ node ] . system_power [ idx ] = entry . system_power_consumption ?? [ ] ;
473473 // nodes_info[node].gpu_power[idx] = (entry.gpu_power_consumption ?? []).map(v => v / 1000);
474474 nodes_info [ node ] . cpu_power [ idx ] = entry . cpu_power_consumption ?? [ ] ;
475475 // nodes_info[node].gpu_mem[idx] = entry.gpu_memory_usage ?? [];
476476 // nodes_info[node].gpu_usage[idx] = entry.gpu_usage ?? [];
477+ nodes_info [ node ] . memory_usage [ idx ] = entry . memory_usage ?? [ ] ;
477478 nodes_info [ node ] . temperature [ idx ] = entry . temperature ?? [ ] ;
478479 nodes_info [ node ] . cpu_usage [ idx ] = entry . cpu_usage ?? [ ] ;
479480 nodes_info [ node ] . dram_usage [ idx ] = entry . dram_usage ?? [ ] ;
@@ -605,19 +606,90 @@ function startRealTimePolling(isRealTime = true) {
605606 realTimeIntervalId = setInterval ( fetchAndUpdate , intervalMs ) ;
606607}
607608
609+ // async function loadHistoricalAcrossRanges() {
610+ // const start = document.getElementById('startTime')?.value;
611+ // const end = document.getElementById('endTime')?.value;
612+ // if (!start || !end) {
613+ // console.warn('Start or End time is missing.');
614+ // return;
615+ // }
616+
617+ // const data = await fetchAllNodeRanges(start, end);
618+ // request = new Simulation(Promise.resolve(data)); // you might need to adapt Simulation constructor
619+ // initdraw();
620+ // initTimeElement();
621+ // }
622+
608623async function loadHistoricalAcrossRanges ( ) {
609624 const start = document . getElementById ( 'startTime' ) ?. value ;
610625 const end = document . getElementById ( 'endTime' ) ?. value ;
626+
611627 if ( ! start || ! end ) {
612628 console . warn ( 'Start or End time is missing.' ) ;
613629 return ;
614630 }
615631
616- const data = await fetchAllNodeRanges ( start , end ) ;
617- request = new Simulation ( Promise . resolve ( data ) ) ; // you might need to adapt Simulation constructor
618- initdraw ( ) ;
619- initTimeElement ( ) ;
632+ if ( document . getElementById ( 'realTimeRange' ) . value == - 3 ) {
633+ const data = await fetchDataAndProcess ( { } , false ) ;
634+
635+ // Convert to timestamps (milliseconds)
636+ const startDate = new Date ( start ) . getTime ( ) * 1e6 ; // Convert to milliseconds
637+ const endDate = new Date ( end ) . getTime ( ) * 1e6 ; // Convert to milliseconds
638+
639+ console . log ( 'Start date:' , startDate , 'End date:' , endDate ) ;
640+ console . log ( 'Data timestamps:' , data . time_stamp ) ;
641+ // Ensure time_stamp is in milliseconds
642+ data . time_stamp = data . time_stamp . map ( d => d instanceof Date ? d . getTime ( ) : d ) ;
643+ console . log ( 'Data timestamps:' , data . time_stamp ) ;
644+
645+ const validIndexes = data . time_stamp
646+ . map ( ( t , i ) => ( { t, i } ) )
647+ . filter ( ( { t } ) => t >= startDate && t <= endDate )
648+ . map ( ( { i } ) => i ) ;
649+
650+ const filteredTimestamps = validIndexes . map ( i => data . time_stamp [ i ] ) ;
651+
652+ const filteredNodesInfo = { } ;
653+ for ( const [ node , metrics ] of Object . entries ( data . nodes_info ) ) {
654+ const filteredMetrics = { } ;
655+ for ( const [ key , arr ] of Object . entries ( metrics ) ) {
656+ filteredMetrics [ key ] = Array . isArray ( arr )
657+ ? validIndexes . map ( i => arr [ i ] )
658+ : arr ;
659+ }
660+ filteredNodesInfo [ node ] = filteredMetrics ;
661+ }
662+
663+ const filteredData = {
664+ ...data ,
665+ time_stamp : filteredTimestamps ,
666+ nodes_info : filteredNodesInfo ,
667+ } ;
668+
669+ request = new Simulation ( Promise . resolve ( filteredData ) ) ;
670+
671+ d3 . select ( '#chartContainer' ) . selectAll ( "*" ) . remove ( ) ;
672+ updateProcess ( {
673+ percentage : 5 ,
674+ text : 'Load UI...'
675+ } )
676+ initMenu ( ) ;
677+ updateProcess ( {
678+ percentage : 15 ,
679+ text : 'Preprocess data...'
680+ } ) ;
681+ initdraw ( ) ;
682+ initTimeElement ( ) ;
683+ }
684+ else {
685+ // Regular fetch mode
686+ const data = await fetchAllNodeRanges ( start , end ) ;
687+ request = new Simulation ( Promise . resolve ( data ) ) ;
688+ initdraw ( ) ;
689+ initTimeElement ( ) ;
690+ }
620691}
692+
621693function loadHistoricalData ( ) {
622694 if ( realTimeIntervalId ) clearInterval ( realTimeIntervalId ) ;
623695
@@ -635,24 +707,59 @@ function loadHistoricalData() {
635707 initTimeElement ( ) ;
636708}
637709
638- function loadSampleData ( ) {
710+ async function loadSampleData ( ) {
639711 if ( realTimeIntervalId ) clearInterval ( realTimeIntervalId ) ;
640- request = new Simulation ( fetchDataAndProcess ( { } , false ) ) ;
641- initdraw ( ) ;
712+ const data = await fetchDataAndProcess ( { } , false ) ;
713+
714+ // Store min/max times from sample data
715+ const timestamps = data . time_stamp . map ( ts => new Date ( ts / 1e6 ) ) ; // Convert from ns to ms
716+ const toUTCMinus6 = ts => new Date ( ts - 6 * 60 * 60 * 1000 ) ;
717+
718+ sampleMinTime = toUTCMinus6 ( Math . min ( ...timestamps ) ) ;
719+ sampleMaxTime = toUTCMinus6 ( Math . max ( ...timestamps ) ) ;
720+
721+
722+
723+ if ( sampleMinTime && sampleMaxTime ) {
724+ const startInput = d3 . select ( '#startTime' ) . node ( ) ;
725+ const endInput = d3 . select ( '#endTime' ) . node ( ) ;
726+
727+ // Format: YYYY-MM-DDTHH:mm
728+ const toInputValue = d => d . toISOString ( ) . slice ( 0 , 16 ) ;
729+
730+ const minStr = toInputValue ( sampleMinTime ) ;
731+ const maxStr = toInputValue ( sampleMaxTime ) ;
732+
733+ // 👇 Limit the allowed selectable range
734+ startInput . min = endInput . min = minStr ;
735+ startInput . max = endInput . max = maxStr ;
736+
737+ // Optional: set default values
738+ startInput . value = minStr ;
739+ endInput . value = maxStr ;
740+ }
741+ request = new Simulation ( Promise . resolve ( data ) ) ;
742+
743+ updateProcess ( {
744+ percentage : 5 ,
745+ text : 'Load UI...'
746+ } )
747+ initMenu ( ) ;
748+ updateProcess ( {
749+ percentage : 15 ,
750+ text : 'Preprocess data...'
751+ } ) ;
752+ initdraw ( ) ;
642753 initTimeElement ( ) ;
643- }
644- d3 . selectAll ( '#navMode li a' ) . on ( 'click' , function ( ) {
645- const mode = d3 . select ( this . parentNode ) . classed ( 'realtime' ) ? 'realTime' : 'historical' ;
646- renderModeMenu ( mode ) ;
647- setTimeout ( ( ) => {
648- if ( mode === 'realTime' ) {
649- startRealTimePolling ( ) ;
650- } else {
651- // loadHistoricalData();
652- loadSampleData ( ) ;
653754 }
654- } , 100 ) ;
655- } ) ;
755+ d3 . selectAll ( '#navMode li a' ) . on ( 'click' , function ( ) {
756+ const mode = d3 . select ( this . parentNode ) . classed ( 'realtime' ) ? 'realTime' : 'historical' ;
757+ renderModeMenu ( mode ) ;
758+ setTimeout ( ( ) => {
759+ loadSampleData ( ) ;
760+
761+ } , 100 ) ;
762+ } ) ;
656763
657764$ ( document ) . ready ( function ( ) {
658765 try {
@@ -669,7 +776,7 @@ $(document).ready(function () {
669776 // "gpu_mem", "gpu_usage", "cpu_usage", "dram_usage",
670777 // ];
671778 serviceListattr = [
672- "system_power" , "cpu_power" , "dram_power" , " temperature", "cpu_usage" , "dram_usage " ,
779+ "system_power" , "cpu_power" , "temperature" , "cpu_usage" , "memory_usage " ,
673780 ] ;
674781
675782 serviceLists = serviceListattr . map ( ( key , index ) => ( {
@@ -682,7 +789,7 @@ $(document).ready(function () {
682789 enable : true ,
683790 idroot : index ,
684791 angle : 0 ,
685- range : index == 3 ? [ 0 , 100 ] : [ 0 , 3000 ] ,
792+ range : [ 0 , 3000 ] ,
686793 } ]
687794 } ) ) ;
688795 serviceFullList = [ ] ;
@@ -809,35 +916,35 @@ function updateServiceRanges(data) {
809916
810917 // Initialize min/max for each metric
811918 for ( const metric of metrics ) {
812- rangeMap [ metric ] = { min : Infinity , max : - Infinity } ;
919+ rangeMap [ metric ] = { min : Infinity , max : - Infinity , sum : 0 , count : 0 } ;
813920 }
814-
815921 // Loop through nodes and update min/max values
816922 for ( const node in data . nodes_info ) {
817923 const info = data . nodes_info [ node ] ;
818924 metrics . forEach ( metric => {
819- const values = ( info [ metric ] || [ ] ) . flat ( ) ; // Flatten 2D array if needed
820- for ( const v of values ) {
821- if ( v != null && ! isNaN ( v ) ) {
822- rangeMap [ metric ] . min = Math . min ( rangeMap [ metric ] . min , v ) ;
823- rangeMap [ metric ] . max = Math . max ( rangeMap [ metric ] . max , v ) ;
824- }
825- }
925+ const values = ( info [ metric ] || [ ] ) . flat ( ) ;
926+ rangeMap [ metric ] . min = Math . min ( rangeMap [ metric ] . min , Math . min ( ...values ) ) ;
927+ rangeMap [ metric ] . max = Math . max ( rangeMap [ metric ] . max , Math . max ( ...values ) ) ;
928+ rangeMap [ metric ] . sum += values . reduce ( ( a , b ) => a + b , 0 ) ;
929+ rangeMap [ metric ] . count += values . length ;
826930 } ) ;
827931 }
828932
829- // Update serviceLists.sub.range
830- for ( const service of serviceLists ) {
831- const metric = service . text ;
832- const r = rangeMap [ metric ] ;
833- if ( r . min !== Infinity && r . max !== - Infinity ) {
834- service . sub [ 0 ] . range = [ Math . floor ( r . min ) , Math . ceil ( r . max ) ] ;
835- }
933+ for ( const service of serviceLists ) {
934+ const metric = service . idroot != null ? serviceListattr [ service . idroot ] : service . text ;
935+ const r = rangeMap [ metric ] ;
936+ if ( r && r . min !== Infinity && r . max !== - Infinity ) {
937+ service . sub [ 0 ] . range = [ Math . floor ( r . min ) , Math . ceil ( r . max ) ] ;
938+ service . sub [ 0 ] . defaultThreshold = rangeMap [ metric ] . count > 0
939+ ? Math . floor ( rangeMap [ metric ] . sum / rangeMap [ metric ] . count )
940+ : 0 ;
836941 }
942+ }
837943
838944 console . log ( "Updated ranges:" , serviceLists . map ( s => ( {
839945 metric : s . text ,
840- range : s . sub [ 0 ] . range
946+ range : s . sub [ 0 ] . range ,
947+ defaultThreshold : s . sub [ 0 ] . defaultThreshold
841948 } ) ) ) ;
842949}
843950
0 commit comments