Commit 809d37b8 authored by Jay Dubb's avatar Jay Dubb
Browse files

v1.29.2

-- Fix some error handling bugs when optional fields from acquisition layer are missing, we inconsistently reported to user that data wasn't loaded correctly but other times we'd load it as if there's no error. Distinguish between reporting error when fields are invalid vs reporting a warning when fields are missing. Errors raise negative errors and prevent file data from being accessed displaying message boix instead whereas missing optional fields produce positive errors and allow data to be access and generate only a log message on the cosole and log file.

-- Specifically fix Homer3 not being able to load acquisition files without optional fields stim and aux in both SNIRF and .nirs and not being able to convert from .nirs to snirf.

-- Move acquisition layer error messages from RunClass to the acquisition layer (AcqDataClass, SnirfClass, NirsClass). The previous code would not work when loading faulty .nirs files. Create methods for the acquisition classes to access the correct error message matching the error.

-- Fix error when displaying data from .nirs files by removing incomplete code in MainGUI to handle non-Snirf data.

-- Fix missing error checking in hmrR_BlockAvg_Nirs which didn't handle when stim data is not passed.

-- Bring back logging of matlab version at startup which somehow got lost

-- Add AppSettings configuration in unit tests UnitTestsAll_Nirs and UnitTestsAll_Snirf when running standalone .
parent 5179b23b
Loading
Loading
Loading
Loading
+21 −7
Original line number Diff line number Diff line
@@ -3,6 +3,9 @@ classdef AcqDataClass < matlab.mixin.Copyable
    properties (Access = private)
        logger
    end
    properties (Access = public)
        errmsgs
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % These methods must be implemented in any derived class
@@ -81,14 +84,25 @@ classdef AcqDataClass < matlab.mixin.Copyable
        
        
        % -------------------------------------------------------
        function b = Error(obj)
            if obj.GetError()<0
                b = true;
            elseif obj.GetError()==0
                b = false;
            else
                b = true;
        function err = Error(obj)
            err = obj.GetError();
        end
        
        
        % ---------------------------------------------------------
        function msg = GetErrorMsg(obj)
            msg = '';
            if isempty(obj)
                msg = 'AcqDataClass object is empty';
                return;
            end
            if isempty(obj.errmsgs)
                return;
            end
            if ~obj.GetError()
                return;
            end
            msg = obj.errmsgs{abs(obj.GetError())};
        end
        
        
+140 −72
Original line number Diff line number Diff line
@@ -34,15 +34,15 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            % 
            %           NirsClass with properties:
            % 
            %                      SD: [11 struct]
            %                       t: [126981 double]
            %                       s: [126982 double]
            %                       d: [1269818 double]
            %                     aux: [126981 double]
            %                      SD: [1x1 struct]
            %                       t: [12698x1 double]
            %                       s: [12698x2 double]
            %                       d: [12698x18 double]
            %                     aux: [12698x1 double]
            %               CondNames: {'1'  '2'}
            %                filename: './s1/neuro_run01.nirs'
            %              fileformat: 'mat'
            %         supportedFomats: [11 struct]
            %         supportedFomats: [1x1 struct]
            %                     err: 0
            %
            
@@ -89,6 +89,19 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            obj.d         = [];
            obj.aux       = [];
            obj.CondNames = {};
            
            
            % Initialize non-.nirs variables
            obj.errmsgs = {
                'MATLAB could not load the file.'
                '''d'' is invalid.'
                '''t'' is invalid.'
                '''SD'' is invalid.'
                '''aux'' is invalid.'
                '''s'' is invalid.'
                'error unknown.'
                };
            
        end
        
        
@@ -101,9 +114,10 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
        
        
        % ---------------------------------------------------------
        function err = LoadMat(obj, fname, params)
        function err = LoadMat(obj, fname, ~)
            err = 0;

            try 
                % Arg 1
                if ~exist('fname','var') || ~exist(fname,'file')
                    fname = '';
@@ -122,63 +136,99 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
                
                % Don't reload if not empty
                if ~obj.IsEmpty()
                    err = obj.GetError();     % preserve error state if exiting early
                    return;
                end
                
                warning('off', 'MATLAB:load:variableNotFound');
                fdata = load(fname,'-mat', 'SD','t','d','s','aux','CondNames');
                
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % NOTE: Optional fields have positive error codes if they are
                % missing, but negative error codes if they're not missing but
                % invalid
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                
                
                % Mandatory fields
                err = obj.LoadRawData(fdata, err);
                err = obj.LoadTime(fdata, err);
                err = obj.LoadProbeMeasList(fdata, err);
                                
                % Optional fields
                err = obj.LoadAux(fdata, err);
                err = obj.LoadStims(fdata, err);
                
            catch
                
                
                
            end            
            warning('on', 'MATLAB:load:variableNotFound');
            
        end
        
        
        
        % ---------------------------------------------------------
        function err = LoadRawData(obj, fdata, err)
            if isproperty(fdata,'d')
                obj.d = fdata.d;
                if isempty(obj.d)
                if isempty(obj.d) && err == 0
                    err = -2;
                end
            else
            elseif err == 0
                err = -2;
            end
        end
        
        
        % ---------------------------------------------------------
        function err = LoadTime(obj, fdata, err)
            if isproperty(fdata,'t')
                obj.t = fdata.t;
                if ~isempty(obj.t)
                    obj.errmargin = min(diff(obj.t))/10;
                else
                elseif err == 0
                    err = -3;
                end
            else
            elseif err == 0
                err = -3;
            end
            if length(fdata.t) ~= size(fdata.d,1) && err == 0
                err = -3;
            end            
        end
        
                
        % ---------------------------------------------------------
        function err = LoadProbeMeasList(obj, fdata, err)
            if isproperty(fdata,'SD')
                obj.SetSD(fdata.SD);
                if isempty(obj.SD)
                if isempty(obj.SD) && err == 0
                    err = -4;
                end
            else
            elseif err == 0
                err = -4;
            end
        end
        
        
            % Optional fields
        % ---------------------------------------------------------
        function err = LoadAux(obj, fdata, err)
            if isproperty(fdata,'aux')
                obj.aux = fdata.aux;
            else
                obj.aux = [];
            end            
            if obj.LoadStims(fname, fdata)<0 
                err = -4;
            elseif err==0
                err = 5;
            end
        end
       
        
                
        % ---------------------------------------------------------
        function err = LoadStims(obj, fname, fdata)
            err = 0;
            
            if ~exist('fdata','var') || isempty(fdata)
                % Arg 1
                if ~exist('fname','var') || ~exist(fname,'file')
                    fname = '';
                end
        function err = LoadStims(obj, fdata, err)                        
            if ischar(fdata)
                fname = fdata;
                
                % Do some error checking
                if ~isempty(fname)
@@ -190,24 +240,32 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
                    err = -1;
                    return;
                end                              
                
                % Don't reload if not empty
                if ~obj.IsEmpty()
                    return;
                end
                
                warning('off', 'MATLAB:load:variableNotFound');
                fdata = load(fname,'-mat', 's','CondNames');
                fdata = load(fname,'-mat', 's','CondNames','t');
            end
        
            if ~isproperty(fdata,'s')
                if err==0 
                    err = 6; 
                end
                return;
            end
            
            if isproperty(fdata,'s')
            obj.s = fdata.s;
            else
                obj.s = [];
            if size(fdata.s,1) ~= size(fdata.t)
                if err==0 
                    err = -6; 
                end
                return;
            end
            if isproperty(fdata,'CondNames')
                obj.CondNames = fdata.CondNames;
                if size(fdata.s,1) ~= length(fdata.CondNames)
                    if err==0
                        err = -6;
                    end
                    return;
                end
            else
                obj.InitCondNames();
            end
@@ -261,7 +319,7 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            % If we're working off the snirf file instead of loading everything into memory
            % then we have to load stim here from file before accessing it.
            if strcmpi(obj.GetDataStorageScheme(), 'files')
                obj.LoadStims(obj.GetFilename());
                obj.LoadStims(obj.GetFilename(), 0);
            end
            
            % Generate new instance of NirsClass
@@ -534,6 +592,16 @@ classdef NirsClass < AcqDataClass & FileLoadSaveClass
            ich={ich};
        end

        
        % ---------------------------------------------------------
        function t = GetAuxiliaryTime(obj)
            t = [];
            if isempty(obj.aux)
                return;
            end
            t = obj.t;
        end
        
    end
    
    
+63 −9
Original line number Diff line number Diff line
@@ -31,13 +31,11 @@ classdef AuxClass < FileLoadSaveClass
                obj.dataTimeSeries = [];
                obj.time = [];
            end                        
            
        end
        
        
        % -------------------------------------------------------
        function err = LoadHdf5(obj, fileobj, location)
            err = 0;
            
            % Arg 1
            if ~exist('fileobj','var') || (ischar(fileobj) && ~exist(fileobj,'file'))
@@ -51,7 +49,7 @@ classdef AuxClass < FileLoadSaveClass
                location = ['/',location];
            end
            
            % Error checking            
            % Error checking for file existence
            if ~isempty(fileobj) && ischar(fileobj)
                obj.SetFilename(fileobj);
            elseif isempty(fileobj)
@@ -66,8 +64,10 @@ classdef AuxClass < FileLoadSaveClass
            try               
                % Open group
                [gid, fid] = HDF5_GroupOpen(fileobj, location);
                if gid<0
                    err = -1;
                
                % Absence of optional aux field raises error > 0
                if gid.double < 0
                    err = 1;
                    return;
                end
                
@@ -76,10 +76,20 @@ classdef AuxClass < FileLoadSaveClass
                obj.time            = HDF5_DatasetLoad(gid, 'time');
                obj.timeOffset      = HDF5_DatasetLoad(gid, 'timeOffset');

                err = obj.ErrorCheck();
                
                % Close group
                HDF5_GroupClose(fileobj, gid, fid);
                
            catch
                err = -2;
                
                if gid.double > 0
                    % If optional aux field exists BUT is in some way invalid it raises error < 0
                    err = -6;
                else
                    err = 1;
                end
                
            end
        end

@@ -182,6 +192,50 @@ classdef AuxClass < FileLoadSaveClass
        end
        
        
        % ----------------------------------------------------------------------------------
        function b = IsEmpty(obj)
            b = true;
            if isempty(obj)
                return
            end
            if isempty(obj.name)
                return
            end
            if isempty(obj.dataTimeSeries)
                return
            end
            if isempty(obj.time)
                return
            end
            if length(obj.dataTimeSeries) ~= length(obj.time)
                return
            end
            b = false;
        end
        
        
        % ----------------------------------------------------------------------------------
        function err = ErrorCheck(obj)
            err = 0;
            if isempty(obj.name)
                err = -1;
                return
            end
            if isempty(obj.dataTimeSeries)
                err = -2;
                return
            end
            if isempty(obj.time)
                err = -3;
                return
            end
            if length(obj.dataTimeSeries) ~= length(obj.time)
                err = -4;
                return
            end
        end
        
        
    end
    
end
+83 −43
Original line number Diff line number Diff line
@@ -143,10 +143,16 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                    end
                    dotnirs = varargin{1};
                    obj.GenSimulatedTimeBases(dotnirs, tfactors);
                    
                    % Required fields
                    for ii=1:length(tfactors)
                        obj.data(ii) = DataClass(obj.nirs_tb(ii).d, obj.nirs_tb(ii).t(:), obj.nirs_tb(ii).SD.MeasList);
                    end
                    obj.probe      = ProbeClass(dotnirs.SD);
                    
                    
                    % Optional fields
                    if isfield(dotnirs,'s')
                        for ii=1:size(dotnirs.s,2)
                            if isfield(dotnirs, 'CondNames')
                                obj.stim(ii) = StimClass(dotnirs.s(:,ii), dotnirs.t(:), dotnirs.CondNames{ii});
@@ -154,14 +160,18 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                                obj.stim(ii) = StimClass(dotnirs.s(:,ii), dotnirs.t(:), num2str(ii));
                            end
                        end
                    obj.probe      = ProbeClass(dotnirs.SD);
                    end
                    if isfield(dotnirs,'aux')
                        for ii=1:size(dotnirs.aux,2)
                            obj.aux(ii) = AuxClass(dotnirs.aux(:,ii), dotnirs.t(:), sprintf('aux%d',ii));
                        end
                    end
                    
                    % Add metadatatags
                    % Add required field metadatatags that has no .nirs
                    % equivalent 
                    obj.metaDataTags   = MetaDataTagsClass();

                    
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % obj = SnirfClass(data, stim);
                % obj = SnirfClass(data, stim, probe);
@@ -244,7 +254,17 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            obj.probe          = ProbeClass().empty();
            obj.aux            = AuxClass().empty();
            
            % Initialize non-SNIRF variables
            obj.stim0          = StimClass().empty();            
            obj.errmsgs = {
                'MATLAB could not load the file.'
                '''formatVersion'' is invalid.'
                '''metaDataTags'' is invalid.'
                '''data'' is invalid.'
                '''stim'' is invalid.'
                '''probe'' is invalid.'
                '''aux'' is invalid.'
                };
        end
        
               
@@ -397,12 +417,10 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.stim)
                    obj.stim(ii) = StimClass;
                end
                if obj.stim(ii).LoadHdf5(fileobj, [obj.location, '/stim', num2str(ii)]) < 0
                err = obj.stim(ii).LoadHdf5(fileobj, [obj.location, '/stim', num2str(ii)]);
                if err ~= 0
                    obj.stim(ii).delete();
                    obj.stim(ii) = [];
                    if ii==1
                        err = 1;  % Absence of optional field raises error > 0
                    end
                    break;
                end
                ii=ii+1;
@@ -415,7 +433,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.stim0)
                    obj.stim0(ii) = StimClass;
                end
                if obj.stim0(ii).LoadHdf5(fileobj, [obj.location, '/stim0', num2str(ii)]) < 0
                if obj.stim0(ii).LoadHdf5(fileobj, [obj.location, '/stim0', num2str(ii)]) ~= 0
                    obj.stim0(ii).delete();
                    obj.stim0(ii) = [];
                    break;
@@ -443,12 +461,10 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                if ii > length(obj.aux)
                    obj.aux(ii) = AuxClass;
                end
                if obj.aux(ii).LoadHdf5(fileobj, [obj.location, '/aux', num2str(ii)]) < 0
                err = obj.aux(ii).LoadHdf5(fileobj, [obj.location, '/aux', num2str(ii)]);
                if err ~= 0
                    obj.aux(ii).delete();
                    obj.aux(ii) = [];
                    if ii==1
                        err = 1;  % Error code for no optional field is > 0
                    end
                    break;
                end
                ii=ii+1;
@@ -479,6 +495,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            
            % Don't reload if not empty
            if ~obj.IsEmpty()
                err = obj.GetError();     % preserve error state if exiting early
                return;
            end
            
@@ -491,37 +508,46 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
                [obj.gid, obj.fid] = HDF5_GroupOpen(fileobj, '/');
                
                
                if obj.SetLocation() < 0 & err == 0
                if obj.SetLocation() < 0 && err == 0
                    err = -1;
                end
                
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                % NOTE: Optional fields have positive error codes if they are
                % missing, but negative error codes if they're not missing but 
                % invalid
                %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
                
                %%%% Load formatVersion
                if obj.LoadFormatVersion() < 0 & err == 0
                if obj.LoadFormatVersion() < 0 && err == 0
                    err = -2;
                end

                %%%% Load metaDataTags
                if obj.LoadMetaDataTags(obj.fid) < 0 & err == 0
                    err = -3;
                if obj.LoadMetaDataTags(obj.fid) < 0 && err == 0
                    % Here a positive return value means that invalid data meta tags 
                    % should NOT be a show stopper if we can help it, if the reste of the data 
                    % is valid. So just let user know they're invalid with a warning.
                    err = 3;
                end

                %%%% Load data
                if obj.LoadData(obj.fid) < 0 & err == 0
                if obj.LoadData(obj.fid) < 0 && err == 0
                    err = -4;
                end

                %%%% Load stim
                if obj.LoadStim(obj.fid) < 0 & err == 0
                if obj.LoadStim(obj.fid) < 0 && err == 0
                    err = -5;
                end

                %%%% Load probe
                if obj.LoadProbe(obj.fid) < 0 & err == 0
                if obj.LoadProbe(obj.fid) < 0 && err == 0
                    err = -6;
                end

                %%%% Load aux. This is an optional field
                if obj.LoadAux(obj.fid) < 0 & err == 0
                if obj.LoadAux(obj.fid) < 0 && err == 0
                    err = -7;
                end
                
@@ -795,6 +821,7 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
            end
            B = true;
        end
        
    end
    
    
@@ -936,6 +963,19 @@ classdef SnirfClass < AcqDataClass & FileLoadSaveClass
        end
        
        
        % ---------------------------------------------------------
        function t = GetAuxiliaryTime(obj)
            t = [];
            if isempty(obj.aux)
                return;
            end
            if obj.aux(1).IsEmpty()
                return;
            end
            t = obj.aux(1).GetTime();
        end
        
        
        % ---------------------------------------------------------
        function ml = GetMeasList(obj, iBlk)
            ml = [];
+35 −9
Original line number Diff line number Diff line
@@ -68,7 +68,6 @@ classdef StimClass < FileLoadSaveClass
        
        % -------------------------------------------------------
        function err = LoadHdf5(obj, fileobj, location)
            err = 0;
            
            % Arg 1
            if ~exist('fileobj','var') || (ischar(fileobj) && ~exist(fileobj,'file'))
@@ -82,7 +81,7 @@ classdef StimClass < FileLoadSaveClass
                location = ['/',location];
            end
            
            % Error checking            
            % Error checking for file existence
            if ~isempty(fileobj) && ischar(fileobj)
                obj.SetFilename(fileobj);
            elseif isempty(fileobj)
@@ -98,6 +97,12 @@ classdef StimClass < FileLoadSaveClass
                % Open group
                [gid, fid] = HDF5_GroupOpen(fileobj, location);

                % Absence of optional aux field raises error > 0
                if gid.double < 0
                    err = 1;
                    return;
                end
                
                % Load datasets
                obj.name   = HDF5_DatasetLoad(gid, 'name');
                obj.data   = HDF5_DatasetLoad(gid, 'data', [], '2D');
@@ -106,16 +111,22 @@ classdef StimClass < FileLoadSaveClass
                    obj.data = [];
                end
                
                err = obj.ErrorCheck();

                % Close group
                HDF5_GroupClose(fileobj, gid, fid);
                
            catch ME
                obj.updateStates();
                
            catch
                
                err = -1;
                return
                if gid.double > 0
                    err = -2;
                else
                    err = 1;
                end
                
            end
            obj.updateStates();
        end
        
        
@@ -193,6 +204,8 @@ classdef StimClass < FileLoadSaveClass
            B = true;
        end
        
        
        % -------------------------------------------------------
        function updateStates(obj)
            % Generate or regenerate a state list compatible with the data
            % array. Match up existing states with new list of time points
@@ -217,6 +230,19 @@ classdef StimClass < FileLoadSaveClass
                end
            end
        end
        
        
        
        % ----------------------------------------------------------------------------------
        function err = ErrorCheck(obj)
            err = 0;
            if size(obj.data, 2)<3
                err = -2;
                return;
            end
        end
        
                
    end
    
    
Loading