From 716e7b498bf0b85fffc6c41137776a3cb2b45820 Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 10:03:29 +0200 Subject: [PATCH 1/7] Fix: do not show negative disk space --- src/components/SidePanel/StartStop.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidePanel/StartStop.tsx b/src/components/SidePanel/StartStop.tsx index 5dae4447..b935f7a8 100644 --- a/src/components/SidePanel/StartStop.tsx +++ b/src/components/SidePanel/StartStop.tsx @@ -99,7 +99,7 @@ export default () => { const action = () => { getFreeSpace(diskFullTrigger, sessionFolder).then(space => { - setFreeSpace(space); + setFreeSpace(Math.max(0, space)); }); }; action(); From 08003f231eaddbbdc40f2f79fd2a7048f8bdc00c Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 10:03:56 +0200 Subject: [PATCH 2/7] Fix: use check of disk is full when recording indefinitly --- src/actions/deviceActions.ts | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/actions/deviceActions.ts b/src/actions/deviceActions.ts index 999efa18..98f50e8f 100644 --- a/src/actions/deviceActions.ts +++ b/src/actions/deviceActions.ts @@ -302,6 +302,24 @@ export const open = dispatch(setSavePending(true)); } + const shouldCheckDiskFull = + performance.now() - lastDiskFullCheck > 10_000; + + if (shouldCheckDiskFull) { + lastDiskFullCheck = performance.now(); + isDiskFull( + getDiskFullTrigger(getState()), + getSessionRootFolder(getState()) + ).then(isFull => { + if (isFull) { + logger.warn( + 'Session stopped. Disk full trigger detected' + ); + dispatch(samplingStop()); + } + }); + } + const durationInMicroSeconds = convertTimeToSeconds( getState().app.dataLogger.duration, @@ -311,24 +329,6 @@ export const open = if (samplingRunning) { dispatch(samplingStop()); } - - const shouldCheckDiskFull = - performance.now() - lastDiskFullCheck > 10_000; - - if (shouldCheckDiskFull) { - lastDiskFullCheck = performance.now(); - isDiskFull( - getDiskFullTrigger(getState()), - getSessionRootFolder(getState()) - ).then(isFull => { - if (isFull) { - logger.warn( - 'Session stopped. Disk full trigger detected' - ); - dispatch(samplingStop()); - } - }); - } } }; From 22782c40d2852f7e2936ba320f622b6107431118 Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 10:04:10 +0200 Subject: [PATCH 3/7] Feat: changelog --- Changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changelog.md b/Changelog.md index bf8ebc41..0944fcc6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,6 +9,8 @@ - Chart frame trotting for slower machines. - Clearing session files on close in some special cases. +- When recording indefinitely **Disk full trigger** stop recording when disk + is full ## 4.0.0 - 2024-03-13 From f0d2e82a4b1ec6e91300d530925ea60c1c1b1d00 Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 12:07:49 +0200 Subject: [PATCH 4/7] Fix: check if disk is full before start new session --- src/components/SidePanel/StartStop.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/components/SidePanel/StartStop.tsx b/src/components/SidePanel/StartStop.tsx index b935f7a8..997af392 100644 --- a/src/components/SidePanel/StartStop.tsx +++ b/src/components/SidePanel/StartStop.tsx @@ -9,6 +9,7 @@ import { useDispatch, useSelector } from 'react-redux'; import { ConfirmationDialog, Group, + logger, StartStopButton, telemetry, } from '@nordicsemiconductor/pc-nrfconnect-shared'; @@ -88,6 +89,20 @@ export default () => { mode, samplesPerSecond: DataManager().getSamplesPerSecond(), }); + + const space = Math.max( + 0, + await getFreeSpace(diskFullTrigger, sessionFolder) + ); + + setFreeSpace(space); + + if (space === 0) { + logger.warn('Disk is full. Unable to start new session'); + setShowDialog(false); + return; + } + dispatch(samplingStart()); setShowDialog(false); }; From 34badbda1d172fb232c44869dbf849ceac1bffe0 Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 12:51:08 +0200 Subject: [PATCH 5/7] Feat: update free space and trigger check on every page write --- src/actions/deviceActions.ts | 36 ++++++++++++-------------- src/components/SidePanel/StartStop.tsx | 13 ++++++++-- src/globals.ts | 2 ++ src/utils/FileBuffer.ts | 25 ++++++++++++++++-- 4 files changed, 53 insertions(+), 23 deletions(-) diff --git a/src/actions/deviceActions.ts b/src/actions/deviceActions.ts index 98f50e8f..4efa68b4 100644 --- a/src/actions/deviceActions.ts +++ b/src/actions/deviceActions.ts @@ -79,6 +79,7 @@ import { setSpikeFilter as persistSpikeFilter } from '../utils/persistentStore'; let device: null | SerialDevice = null; let updateRequestInterval: NodeJS.Timeout | undefined; +let releaseFileWriteListener: (() => void) | undefined; export const setupOptions = (recordingMode: RecordingMode): AppThunk> => @@ -99,6 +100,21 @@ export const setupOptions = DataManager().initializeTriggerSession(60); break; } + + releaseFileWriteListener?.(); + releaseFileWriteListener = DataManager().onFileWrite?.(() => { + isDiskFull( + getDiskFullTrigger(getState()), + getSessionRootFolder(getState()) + ).then(isFull => { + if (isFull) { + logger.warn( + 'Session stopped. Disk full trigger detected' + ); + dispatch(samplingStop()); + } + }); + }); } catch (err) { logger.error(err); } @@ -145,6 +161,7 @@ export const samplingStop = dispatch(samplingStoppedAction()); await device.ppkAverageStop(); stopPreventSleep(); + releaseFileWriteListener?.(); }; export const updateSpikeFilter = (): AppThunk => (_, getState) => { @@ -207,7 +224,6 @@ export const open = let prevBits = 0; let nbSamples = 0; let nbSamplesTotal = 0; - let lastDiskFullCheck = 0; const onSample = ({ value, bits }: SampleValues) => { const { @@ -302,24 +318,6 @@ export const open = dispatch(setSavePending(true)); } - const shouldCheckDiskFull = - performance.now() - lastDiskFullCheck > 10_000; - - if (shouldCheckDiskFull) { - lastDiskFullCheck = performance.now(); - isDiskFull( - getDiskFullTrigger(getState()), - getSessionRootFolder(getState()) - ).then(isFull => { - if (isFull) { - logger.warn( - 'Session stopped. Disk full trigger detected' - ); - dispatch(samplingStop()); - } - }); - } - const durationInMicroSeconds = convertTimeToSeconds( getState().app.dataLogger.duration, diff --git a/src/components/SidePanel/StartStop.tsx b/src/components/SidePanel/StartStop.tsx index 997af392..a81364fd 100644 --- a/src/components/SidePanel/StartStop.tsx +++ b/src/components/SidePanel/StartStop.tsx @@ -4,7 +4,7 @@ * SPDX-License-Identifier: LicenseRef-Nordic-4-Clause */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { ConfirmationDialog, @@ -57,6 +57,8 @@ const calcFileSizeString = (sampleFreq: number, durationSeconds: number) => { export default () => { const dispatch = useDispatch(); + + const onWriteListener = useRef<() => void>(); const scopePane = useSelector(isScopePane); const dataLoggerPane = useSelector(isDataLoggerPane); const recordingMode = useSelector(getRecordingMode); @@ -103,8 +105,14 @@ export default () => { return; } - dispatch(samplingStart()); setShowDialog(false); + await dispatch(samplingStart()); + onWriteListener.current?.(); + onWriteListener.current = DataManager().onFileWrite(() => { + getFreeSpace(diskFullTrigger, sessionFolder).then(s => { + setFreeSpace(Math.max(0, s)); + }); + }); }; const [freeSpace, setFreeSpace] = useState(0); @@ -143,6 +151,7 @@ export default () => { stopText="Stop" onClick={async () => { if (samplingRunning) { + onWriteListener.current?.(); dispatch(samplingStop()); telemetry.sendEvent('StopSampling', { mode: recordingMode, diff --git a/src/globals.ts b/src/globals.ts index e79b337f..c7bbb5b9 100644 --- a/src/globals.ts +++ b/src/globals.ts @@ -392,6 +392,8 @@ export const DataManager = () => ({ }); }), hasPendingTriggers: () => options.timeReachedTriggers.length > 0, + onFileWrite: (listener: () => void) => + options.fileBuffer?.onFileWrite(listener), }); /** diff --git a/src/utils/FileBuffer.ts b/src/utils/FileBuffer.ts index 3529ae0e..28abdf58 100644 --- a/src/utils/FileBuffer.ts +++ b/src/utils/FileBuffer.ts @@ -29,6 +29,7 @@ export class FileBuffer { #fileBusy = false; #beforeUnload: (event: BeforeUnloadEvent) => Promise; #bufferingListeners: ((event: Promise) => void)[] = []; + #fileWriteListeners: (() => void)[] = []; #freePageBuffers: Uint8Array[] = []; #bufferingRequests: Promise[] = []; #cancelBufferOperations: WeakMap, AbortController> = @@ -125,7 +126,10 @@ export class FileBuffer { throw new Error('Invalid File handle'); return fs .appendFile(this.#fileHandle, activePage.page) - .finally(resolve); + .finally(() => { + this.#fileWriteListeners.forEach(l => l()); + resolve(); + }); }); } else { this.fileOperationTasks.push(() => { @@ -137,7 +141,10 @@ export class FileBuffer { this.#fileHandle, activePage.page.subarray(0, activePage.bytesWritten) ) - .finally(resolve); + .finally(() => { + this.#fileWriteListeners.forEach(l => l()); + resolve(); + }); }); writeBuffer.switchPage(); @@ -577,4 +584,18 @@ export class FileBuffer { } }; } + + onFileWrite(listener: () => void) { + this.#fileWriteListeners.push(listener); + + return () => { + const index = this.#fileWriteListeners.findIndex( + l => l === listener + ); + + if (index !== -1) { + this.#fileWriteListeners.splice(index, 1); + } + }; + } } From 122836c6eeeaadbe754484badb5c957ae5685c1e Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 13:27:14 +0200 Subject: [PATCH 6/7] Feat: limit min disk full trigger to a full page size --- src/components/SidePanel/SessionSettings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SidePanel/SessionSettings.tsx b/src/components/SidePanel/SessionSettings.tsx index c140cc20..a6d5d127 100644 --- a/src/components/SidePanel/SessionSettings.tsx +++ b/src/components/SidePanel/SessionSettings.tsx @@ -76,7 +76,7 @@ export default () => { unit="MB" value={diskFullTrigger} range={{ - min: 1, + min: 10, // 4 * 100_000 * 10 bytes ~= 5.7 MB per page max: 10240, decimals: undefined, step: undefined, From 891c4c6064a9335ed61e471caca09ac37e0ddaf0 Mon Sep 17 00:00:00 2001 From: kylebonnici Date: Thu, 4 Apr 2024 15:21:30 +0200 Subject: [PATCH 7/7] Feedback --- Changelog.md | 4 ++-- src/actions/deviceActions.ts | 2 +- src/components/SidePanel/StartStop.tsx | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0944fcc6..95b64a4a 100644 --- a/Changelog.md +++ b/Changelog.md @@ -9,8 +9,8 @@ - Chart frame trotting for slower machines. - Clearing session files on close in some special cases. -- When recording indefinitely **Disk full trigger** stop recording when disk - is full +- Behavior of **Disk full trigger** when recording for an indefinite period of + time. It now correctly stops sampling when the disk is full. ## 4.0.0 - 2024-03-13 diff --git a/src/actions/deviceActions.ts b/src/actions/deviceActions.ts index 4efa68b4..b3851af8 100644 --- a/src/actions/deviceActions.ts +++ b/src/actions/deviceActions.ts @@ -109,7 +109,7 @@ export const setupOptions = ).then(isFull => { if (isFull) { logger.warn( - 'Session stopped. Disk full trigger detected' + 'Session stopped. Disk full trigger value reached.' ); dispatch(samplingStop()); } diff --git a/src/components/SidePanel/StartStop.tsx b/src/components/SidePanel/StartStop.tsx index a81364fd..0224627b 100644 --- a/src/components/SidePanel/StartStop.tsx +++ b/src/components/SidePanel/StartStop.tsx @@ -100,7 +100,9 @@ export default () => { setFreeSpace(space); if (space === 0) { - logger.warn('Disk is full. Unable to start new session'); + logger.warn( + 'Disk is full. Unable to start sampling. Change the disk full trigger threshold or free up disk memory.' + ); setShowDialog(false); return; }