From a26847b3327ebfbcf600c42975678625e714ff0a Mon Sep 17 00:00:00 2001 From: ThorBarRa Date: Thu, 12 Mar 2026 13:35:27 +0100 Subject: [PATCH] Good to know I'm smarter than google gemini. That code did not run. fixed it. Also fixed a time issue. Start aquisition is now taken from the logged time in the data file or if that is not present the OS creation date. the end aquisition time is the last modification time of the os for that file. --- beamtime/nappFileList.m | 132 ++++++++++++++++++++++++++++++++-------- 1 file changed, 106 insertions(+), 26 deletions(-) diff --git a/beamtime/nappFileList.m b/beamtime/nappFileList.m index da85f03..e1edaf6 100755 --- a/beamtime/nappFileList.m +++ b/beamtime/nappFileList.m @@ -448,6 +448,10 @@ else % Loop through each file in the NEXAFS file list % TmpFileList{2} contains the full paths to all NEXAFS HDF5 files % ii is the loop counter (1, 2, 3, ... up to the total number of files) + + % Initialize a map to hold the original dates for split files + splitFileDatesMap = containers.Map('KeyType', 'char', 'ValueType', 'any'); + for ii = 1:size(TmpFileList{2}, 1) % Get the filename of the current file being processed @@ -456,6 +460,7 @@ else % Read information about the structure of the HDF5 file % This tells us what groups (datasets) are stored in the file fileinfo = h5info(current_file); + % Extract the names of all groups at the root level % Example: {'/scan_1', '/scan_2', '/logs', '/__DATA_TYPES__'} @@ -465,13 +470,21 @@ else % Files with more than 3 groups likely contain multiple scans % (the extra groups beyond scans are typically '/logs' and '/__DATA_TYPES__') if numel(groupnames) > 3 + % 1. Get the true historical dates BEFORE splitting + [orig_cDateOS, orig_mDateOS] = getOSDates(current_file); + % 2. Split the file and get the names of the newly created files % This file contains multiple scans, so split it % The splitH5Scans function will: % - Create separate files for each scan % - Rename each scan to '/scan_1' within its new file % - Delete the original multi-scan file when done - splitH5Scans(current_file); + newlyCreatedFiles = splitH5Scans(current_file); + + % 3. Store the historical dates in the map using the new filenames as keys + for k = 1:length(newlyCreatedFiles) + splitFileDatesMap(newlyCreatedFiles{k}) = {orig_cDateOS, orig_mDateOS}; + end end @@ -571,9 +584,7 @@ if ~isempty(TmpFileList{1}) dateStr = [char(regexp(tmp{29},'((?<=Date=).*)','match')) ' ' char(regexp(tmp{30},'((?<=Time=).*)','match'))]; parsedDate = datevec(dateStr); if size(parsedDate, 2) == 6 - current_file_info.creationDate = parsedDate; - current_file_info.lastModifiedDatenum = datenum(parsedDate); - current_file_info.lastModifiedDatestr = dateStr; + current_file_info.creationDate = parsedDate; end catch end @@ -654,9 +665,9 @@ if ~isempty(TmpFileList{1}) % If the file lacks "Monochromator" in the header and the energy is % incorrectly tagged as ~1453.6 or 1486.6, overwrite it to 1253.6 (Mg Ka). % ========================================================================= - if ~contains(headerText, 'Monochromator', 'IgnoreCase', true) + if ~any(contains(string(fileLines), 'Monochromator', 'IgnoreCase', true)) % Checking both 1453.6 (as requested) and 1486.61 (as seen in your file) - if (abs(globalExEnergy - 1453.6) < 1) || (abs(globalExEnergy - 1486.61) < 1) + if (abs(globalExEnergy - 1453.61) < 1) || (abs(globalExEnergy - 1486.61) < 1) globalExEnergy = 1253.6; end end @@ -701,6 +712,15 @@ if ~isempty(TmpFileList{1}) rName = strtrim(extractAfter(lineStr, '# Region:')); elseif startsWith(lineStr, '# Excitation Energy:') regionEntry.xRayEkin_eV = sscanf(strtrim(extractAfter(lineStr, '# Excitation Energy:')), '%f'); + % ========================================================================= + % OFFLINE XPS BUG FIX (Mg vs Al Source) - REGION OVERRIDE + % ========================================================================= + if ~any(contains(string(fileLines), 'Monochromator', 'IgnoreCase', true)) + if (abs(regionEntry.xRayEkin_eV - 1453.6) < 1) || (abs(regionEntry.xRayEkin_eV - 1486.61) < 1) + regionEntry.xRayEkin_eV = 1253.6; + end + end + % ========================================================================= exEnergyStr = num2str(regionEntry.xRayEkin_eV); elseif startsWith(lineStr, '# Spectrum ID:') specID = sscanf(strtrim(extractAfter(lineStr, '# Spectrum ID:')), '%f'); @@ -710,10 +730,10 @@ if ~isempty(TmpFileList{1}) try dtUTC = datetime(dateStr, 'InputFormat', 'MM/dd/yy HH:mm:ss', 'PivotYear', 2000, 'TimeZone', 'UTC'); dtLocal = datetime(dtUTC, 'TimeZone', 'Europe/Zurich'); - regionEntry.lastModifiedDatenum = datenum(dtLocal); - regionEntry.lastModifiedDatestr = datestr(dtLocal); + % ONLY set the Start Time (creationDate). Leave lastModified as OS default! + regionEntry.creationDate = datevec(dtLocal); catch - regionEntry.lastModifiedDatestr = rawDateStr; + % If parsing fails, it safely falls back to the OS creation date end elseif startsWith(lineStr, '# Pass Energy:') regionEntry.scientaPassEnergy_eV = str2double(extractAfter(lineStr, '# Pass Energy:')); @@ -852,8 +872,26 @@ for ii = 1:size(TmpFileList{2},1) % do this for each list element % Get OS Dates tmpCurrentFile = fullfile(TmpFileList{2}(ii).folder, TmpFileList{2}(ii).name); - [cDateOS, ~] = getOSDates(tmpCurrentFile); + % ========================================================== + % IN-MEMORY DATE RESTORATION + % Check if this file was created by the splitter. If yes, + % retrieve its historical dates from the memory map! + % ========================================================== + if exist('splitFileDatesMap', 'var') && isKey(splitFileDatesMap, TmpFileList{2}(ii).name) + savedDates = splitFileDatesMap(TmpFileList{2}(ii).name); + cDateOS = savedDates{1}; + mDateOS = savedDates{2}; + else + % Otherwise, just read the dates directly from the OS + [cDateOS, mDateOS] = getOSDates(tmpCurrentFile); + end + + % START TIME: OS Creation Date (or mapped historical creation) TmpFileList{2}(ii).creationDate = datevec(cDateOS); + % END TIME: OS Modification Date (or mapped historical modification) + TmpFileList{2}(ii).lastModifiedDatenum = datenum(mDateOS); + TmpFileList{2}(ii).lastModifiedDatestr = mDateOS; + % ========================================================== try @@ -1148,10 +1186,14 @@ for ii = 1:size(TmpFileList{3},1) % do this for each picture in the list % Get OS Dates tmpCurrentFile = fullfile(TmpFileList{3}(ii).folder, TmpFileList{3}(ii).name); [cDateOS, ~] = getOSDates(tmpCurrentFile); + + % START TIME: OS Creation Date TmpFileList{3}(ii).creationDate = datevec(cDateOS); + % END TIME: Robust OS Modification Date + TmpFileList{3}(ii).lastModifiedDatenum = TmpFileList{3}(ii).datenum; + TmpFileList{3}(ii).lastModifiedDatestr = TmpFileList{3}(ii).date; end - %% Merge lists into one % @@ -1674,9 +1716,10 @@ end end -function splitH5Scans(infile) +function newFiles = splitH5Scans(infile) % splitH5Scans - Split an HDF5 file containing multiple scans into separate files % +% % This function takes an HDF5 file with multiple scan groups (scan_1, scan_2, etc.) % and creates separate files for each scan. Each output file contains only one % scan (renamed to scan_1) plus the logs group. The original file is deleted after @@ -1708,13 +1751,11 @@ function splitH5Scans(infile) % Read the structure of the HDF5 file to see what groups it contains % h5info returns information about groups, datasets, and attributes +newFiles = {}; % Initialize the output cell array fileinfo = h5info(infile); -% Get the original file's modification date before we start -% This will be copied to all new files to preserve the original timestamp -orig_file_info = dir(infile); -orig_mod_datetime = orig_file_info.date; % Get as datetime object - +% Get the original file's true OS dates BEFORE we start modifying it! +[orig_cDateOS, orig_mDateOS] = getOSDates(infile); % Extract the names of all groups at the root level of the file % Example: {'/scan_1', '/scan_2', '/logs', '/__DATA_TYPES__'} @@ -1766,6 +1807,10 @@ if numel(groupnames) > 3 % Example: 'C:\Data\0002_20251119_145823_O_Kedge_Nov25_scan_2.h5' outname = fullfile(filepath, sprintf('%s%s_%s%s', prefix, rest, scan_name_clean, ext)); + % Save the new filename to the output array + [~, base_out, ext_out] = fileparts(outname); + newFiles{end+1} = [base_out, ext_out]; + % Copy the entire original file to create the new output file % We'll modify this copy to keep only one scan copyfile(infile, outname); @@ -1804,13 +1849,32 @@ if numel(groupnames) > 3 % Close the HDF5 file to save all changes H5F.close(fid); - % Copy the modification date from the original file to the new file - % Convert MATLAB datenum to timestamp format: YYYYMMDDhhmm.ss - timestamp_str = string(datetime(orig_mod_datetime), 'yyyyMMddHHmm.ss'); - if ismac - touch_cmd = sprintf('touch -t %s "%s"', timestamp_str, outname); - system(touch_cmd); + % ========================================================== + % RESTORE ORIGINAL OS DATES TO THE NEW FILE + % ========================================================== + if ispc + % Windows: Use .NET to set BOTH Creation and Modification time + try + fileObj = System.IO.FileInfo(outname); + fileObj.CreationTime = System.DateTime.Parse(orig_cDateOS); + fileObj.LastWriteTime = System.DateTime.Parse(orig_mDateOS); + catch + end + + elseif ismac || isunix + % Mac/Linux: Use 'touch' to restore Modification time + try + timestamp_str = string(datetime(orig_mDateOS), 'yyyyMMddHHmm.ss'); + system(sprintf('touch -m -t %s "%s"', timestamp_str, outname)); + catch + end + % NOTE: Mac OS makes it incredibly difficult to change the Birth (Creation) + % time via standard commands. However, because we restored the Modification + % time perfectly, our "Paradox Check" in the main loop will automatically + % step in, realize Creation is newer than Modification, and fix the + % Creation date for us in the data table! end + % ========================================================== % Print confirmation message showing which file was created % fprintf('Created: %s\n', outname); @@ -1825,7 +1889,9 @@ else % disp('File has three or fewer groups. No processing required.'); end -end +end + + function [creationDateOS, lastModifiedDate] = getOSDates(fullPath) @@ -1849,15 +1915,29 @@ if ispc catch creationDateOS = d.date; % Fallback end + elseif ismac || isunix % macOS/Linux: Use the stat command to get the "Birth" time [status, cmdOut] = system(['stat -f "%SB" -t "%d-%b-%Y %H:%M:%S" "', fullPath, '"']); if status == 0 creationDateOS = strtrim(cmdOut); + + % ========================================================== + % macOS COPIED FILE WORKAROUND + % If copied/downloaded, macOS sets Birth Time to 'today' but + % keeps the old Modification Time. If Creation > Modification, + % use the older Modification time as the true origin! + % ========================================================== + try + if datetime(creationDateOS) > datetime(lastModifiedDate) + creationDateOS = lastModifiedDate; + end + catch + % Silently continue if datetime conversion fails + end + % ========================================================== else creationDateOS = d.date; % Fallback end -else - creationDateOS = d.date; % Fallback for unknown OS end end \ No newline at end of file