From 247b4e108079e650969b654d3b848489b6c26c22 Mon Sep 17 00:00:00 2001 From: martastn Date: Mon, 30 Mar 2026 14:18:10 +0200 Subject: [PATCH 1/3] dataset download eta --- .../Simulation/Geant4DatasetDownload.tsx | 114 +++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx index 4027aa479..3125cce36 100644 --- a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx +++ b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx @@ -37,9 +37,103 @@ export enum Geant4DatasetsType { FULL } +function formatTime(seconds: number): string { + if (seconds < 1) { + return '<1s'; + } + + const totalSeconds = Math.ceil(seconds); + + if (totalSeconds < 60) { + return `${totalSeconds}s`; + } + + const minutes = Math.floor(totalSeconds / 60); + const remainingSeconds = totalSeconds % 60; + + return `${minutes}m ${remainingSeconds}s`; +} + +export interface Geant4DatasetsProps { + geant4DownloadManagerState: DownloadManagerStatus; + geant4DatasetStates: DatasetStatus[]; + geant4DatasetDownloadStart: () => void; + geant4DatasetType: Geant4DatasetsType; + setGeant4DatasetType: (type: Geant4DatasetsType) => void; +} + +interface SpeedHistory { + lastDone: number; + lastTime: number; + currentSpeed: number; +} + +const SMOOTHING = 0.1; + function DatasetCurrentStatus(props: { status: DatasetStatus }) { const { status } = props; + const [speedHistory, setSpeedHistory] = useState({ + lastDone: status.done ?? 0, + lastTime: Date.now(), + currentSpeed: 0 + }); + + useEffect(() => { + const currentDone = status.done ?? 0; + const currentTime = Date.now(); + + if (status.status === DatasetDownloadStatus.DOWNLOADING) { + setSpeedHistory(prev => { + const timeDelta = (currentTime - prev.lastTime) / 1000; + const bytesDelta = currentDone - prev.lastDone; + + if (bytesDelta > 0 && timeDelta > 0) { + const instSpeed = bytesDelta / timeDelta; + + const newSpeed = + prev.currentSpeed === 0 + ? instSpeed + : prev.currentSpeed * (1 - SMOOTHING) + instSpeed * SMOOTHING; + + return { + lastDone: currentDone, + lastTime: currentTime, + currentSpeed: newSpeed + }; + } + + return { + ...prev, + lastTime: currentTime + }; + }); + } + + if ( + status.status === DatasetDownloadStatus.DONE || + status.status === DatasetDownloadStatus.IDLE + ) { + setSpeedHistory({ + lastDone: status.done ?? 0, + lastTime: Date.now(), + currentSpeed: 0 + }); + } + }, [status.done, status.status]); + + const remainingBytes = (status.total ?? 0) - (status.done ?? 0); + let estimatedTimeRemaining = ''; + + if ( + status.status === DatasetDownloadStatus.DOWNLOADING && + speedHistory.currentSpeed > 0 && + remainingBytes > 0 + ) { + const remainingSeconds = remainingBytes / speedHistory.currentSpeed; + estimatedTimeRemaining = ` (${formatTime(remainingSeconds)})`; + } + const idleIcon = status.cached ? ( ) : ( @@ -68,6 +162,13 @@ function DatasetCurrentStatus(props: { status: DatasetStatus }) { variant='indeterminate' color='warning' /> + ], + [ + DatasetDownloadStatus.IDLE, + ] ]); @@ -75,6 +176,14 @@ function DatasetCurrentStatus(props: { status: DatasetStatus }) { {status.name} + + {status.status === DatasetDownloadStatus.DOWNLOADING && ( + + {estimatedTimeRemaining} + + )} {datasetStatusIcon.get(status.status)} @@ -324,7 +433,10 @@ export function Geant4Datasets() { }}> } - onClick={() => setOpen(!open)}> + onClick={e => { + e.stopPropagation(); + setOpen(!open); + }}> From 3e648ebf1f8e94996c213783c2c38603fa683065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marta=20Stanis=C5=82awska?= Date: Mon, 30 Mar 2026 14:59:33 +0200 Subject: [PATCH 2/3] removed unnecessary export Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../components/Simulation/Geant4DatasetDownload.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx index 3125cce36..cb3d2934e 100644 --- a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx +++ b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx @@ -54,14 +54,6 @@ function formatTime(seconds: number): string { return `${minutes}m ${remainingSeconds}s`; } -export interface Geant4DatasetsProps { - geant4DownloadManagerState: DownloadManagerStatus; - geant4DatasetStates: DatasetStatus[]; - geant4DatasetDownloadStart: () => void; - geant4DatasetType: Geant4DatasetsType; - setGeant4DatasetType: (type: Geant4DatasetsType) => void; -} - interface SpeedHistory { lastDone: number; lastTime: number; From 1fd94b0864fa681652a1196e6a6914cf6ffffb0e Mon Sep 17 00:00:00 2001 From: martastn Date: Mon, 30 Mar 2026 15:08:32 +0200 Subject: [PATCH 3/3] text explaining displayed time --- src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx index cb3d2934e..c88c7974d 100644 --- a/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx +++ b/src/WrapperApp/components/Simulation/Geant4DatasetDownload.tsx @@ -123,7 +123,7 @@ function DatasetCurrentStatus(props: { status: DatasetStatus }) { remainingBytes > 0 ) { const remainingSeconds = remainingBytes / speedHistory.currentSpeed; - estimatedTimeRemaining = ` (${formatTime(remainingSeconds)})`; + estimatedTimeRemaining = ` (est. ${formatTime(remainingSeconds)} remaining)`; } const idleIcon = status.cached ? (