diff --git a/src/components/Connection.tsx b/src/components/Connection.tsx index c262cfc..ee9ce96 100644 --- a/src/components/Connection.tsx +++ b/src/components/Connection.tsx @@ -35,6 +35,12 @@ import { PopoverTrigger, } from "../components/ui/popover"; +interface FormattedData { + timestamp: string; + counter: number | null; + [key: string]: number | null | string; +} + interface ConnectionProps { onPauseChange: (pause: boolean) => void; // Callback to pass pause state to parent dataSteam: (data: number[]) => void; @@ -74,6 +80,8 @@ const Connection: React.FC = ({ const [elapsedTime, setElapsedTime] = useState(0); // State to store the recording duration const timerIntervalRef = useRef(null); // Type for Node.js environment const recordingIntervalRef = useRef(null); + const timerIntervalRef = useRef(null); // Type for Node.js environment + const recordingIntervalRef = useRef(null); const [customTime, setCustomTime] = useState(""); // State to store the custom stop time input const endTimeRef = useRef(null); // Ref to store the end time of the recording const startTimeRef = useRef(null); // Ref to store the start time of the recording @@ -180,6 +188,7 @@ const Connection: React.FC = ({ if (!info || !info.usbVendorId) { return "Port with no info"; } + // console.log(info); // First, check if the board exists in BoardsList const board = BoardsList.find( @@ -312,6 +321,7 @@ const Connection: React.FC = ({ try { // Loop while the device is connected while (isConnectedRef.current) { + // Loop while the device is connected const streamData = await readerRef.current?.read(); // Read data from the device if (streamData?.done) { // Check if the data stream has ended @@ -392,7 +402,8 @@ const Connection: React.FC = ({ } }; - const convertToCSV = (data: any[]): string => { + // Function to convert data to CSV format + const convertToCSV = (data: FormattedData[]): string => { if (data.length === 0) return ""; const header = Object.keys(data[0]); @@ -417,14 +428,13 @@ const Connection: React.FC = ({ if (isRecordingRef.current) { stopRecording(); // Stop the recording if it is already on } else { - // Start a new recording session - isRecordingRef.current = true; // Set the recording state to true - const now = new Date(); // Get the current date and time - const nowTime = now.getTime(); // Get the current time in milliseconds - startTimeRef.current = nowTime; // Store the start time of the recording - setElapsedTime(0); // Reset elapsed time for display - timerIntervalRef.current = setInterval(checkRecordingTime, 1000); // Start a timer to check recording duration every second - setrecData(true); // Set the state indicating recording data is being collected + isRecordingRef.current = true; // Start recording + const now = new Date(); + startTimeRef.current = now.getTime(); + setElapsedTime(0); + timerIntervalRef.current = setInterval(checkRecordingTime, 1000); + + setrecData(true); // Initialize IndexedDB for this recording session try { @@ -438,6 +448,16 @@ const Connection: React.FC = ({ ); } + // Start reading and saving data + recordingIntervalRef.current = setInterval(() => { + const data = bufferRef.current; // Use bufferRef which stores actual data + saveDataDuringRecording(data); // Save the data to IndexedDB + bufferRef.current = []; // Clear the buffer after saving + }, 1000); // Save data every 1 second or adjust the interval as needed + "Failed to initialize storage. Recording may not be saved." + ); + } + // Start reading and saving data recordingIntervalRef.current = setInterval(() => { const data = bufferRef.current; // Use bufferRef which stores actual data @@ -491,18 +511,39 @@ const Connection: React.FC = ({ startTimeRef.current ).toLocaleTimeString(); const endTimeString = endTime.toLocaleTimeString(); + const endTime = new Date(); // Capture the end time + const recordedFilesCount = (await getAllDataFromIndexedDB()).length; + // Check if startTimeRef.current is not null before using it + if (startTimeRef.current !== null) { + // Format the start and end times as readable strings + const startTimeString = new Date( + startTimeRef.current + ).toLocaleTimeString(); + const endTimeString = endTime.toLocaleTimeString(); + + // Calculate the duration of the recording + const durationInSeconds = Math.floor( + (endTime.getTime() - startTimeRef.current) / 1000 + ); // Calculate the duration of the recording const durationInSeconds = Math.floor( (endTime.getTime() - startTimeRef.current) / 1000 ); + // Close IndexedDB reference + if (indexedDBRef.current) { + indexedDBRef.current.close(); + indexedDBRef.current = null; // Reset the reference + } // Close IndexedDB reference if (indexedDBRef.current) { indexedDBRef.current.close(); indexedDBRef.current = null; // Reset the reference } + const allData = await getAllDataFromIndexedDB(); + setHasData(allData.length > 0); const allData = await getAllDataFromIndexedDB(); setHasData(allData.length > 0); @@ -529,6 +570,42 @@ const Connection: React.FC = ({ setIsRecordButtonDisabled(true); }; + // Call this function when your component mounts or when you suspect the data might change + useEffect(() => { + const checkDataAndConnection = async () => { + // Check if data exists in IndexedDB + const allData = await getAllDataFromIndexedDB(); + setHasData(allData.length > 0); + + // Disable the record button if there is data in IndexedDB and device is connected + setIsRecordButtonDisabled(allData.length > 0 || !isConnected); + }; + + checkDataAndConnection(); + }, [isConnected]); + // Display the toast with all the details + toast.success("Recording completed successfully", { + description: ( +
+

Start Time: {startTimeString}

+

End Time: {endTimeString}

+

Recording Duration: {formatDuration(durationInSeconds)}

+

Samples Recorded: {recordedFilesCount}

+
+ ), + }); + } else { + console.error("Start time is null. Unable to calculate duration."); + toast.error("Recording start time was not captured."); + } + + // Reset the recording state + isRecordingRef.current = false; + setElapsedTime(0); + setrecData(false); + setIsRecordButtonDisabled(true); + }; + // Call this function when your component mounts or when you suspect the data might change useEffect(() => { const checkDataAndConnection = async () => { @@ -546,7 +623,7 @@ const Connection: React.FC = ({ // Add this function to save data to IndexedDB during recording const saveDataDuringRecording = async (data: number[][]) => { if (!isRecordingRef.current || !indexedDBRef.current) return; - + try { const tx = indexedDBRef.current.transaction(["adcReadings"], "readwrite"); const store = tx.objectStore("adcReadings"); @@ -569,7 +646,7 @@ const Connection: React.FC = ({ console.error("Error saving data during recording:", error); } }; - + // Function to format time from seconds into a "MM:SS" string format const formatTime = (seconds: number): string => { @@ -630,7 +707,11 @@ const Connection: React.FC = ({ // Start a readwrite transaction on the "adcReadings" object store const tx = db.transaction(["adcReadings"], "readwrite"); const store = tx.objectStore("adcReadings"); + const store = tx.objectStore("adcReadings"); + await store.clear(); + console.log("All data deleted from IndexedDB"); + toast.success("Recorded file is deleted."); await store.clear(); console.log("All data deleted from IndexedDB"); toast.success("Recorded file is deleted."); @@ -639,9 +720,15 @@ const Connection: React.FC = ({ const allData = await getAllDataFromIndexedDB(); setHasData(allData.length > 0); setIsRecordButtonDisabled(false); + // Check if there is any data left in the database after deletion + const allData = await getAllDataFromIndexedDB(); + setHasData(allData.length > 0); + setIsRecordButtonDisabled(false); } catch (error) { console.error("Error deleting data from IndexedDB:", error); toast.error("Failed to delete data. Please try again."); + console.error("Error deleting data from IndexedDB:", error); + toast.error("Failed to delete data. Please try again."); } }; // Function to retrieve all data from the IndexedDB @@ -692,15 +779,18 @@ const Connection: React.FC = ({ // Loop through the channels array based on canvasCount for (let i = 0; i < canvasCount; i++) { + const channelKey = `channel_${i + 1}`; // Create a dynamic key for each channel const channelKey = `channel_${i + 1}`; // Create a dynamic key for each channel dynamicChannels[channelKey] = channels[i] !== undefined ? channels[i] : null; // Handle missing data + channels[i] !== undefined ? channels[i] : null; // Handle missing data } - + return { timestamp: item.timestamp, ...dynamicChannels, // Spread the dynamic channels into the result object counter: item.counter || null, // Include the counter if available + counter: item.counter || null, // Include the counter if available }; }); @@ -717,7 +807,7 @@ const Connection: React.FC = ({ ).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}-${String( now.getSeconds() ).padStart(2, "0")}`; - + // Use the timestamp in the filename const filename = `recorded_data_${formattedTimestamp}.csv`; saveAs(blob, filename); // Trigger download