LoadArgs.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. classdef LoadArgs < handle
  2. %LOADARGS parser for file loading arguments
  3. properties (GetAccess = private, Constant = true)
  4. wellVal = 1;
  5. electrodeVal = 2;
  6. timespanVal = 3;
  7. dimensionsVal = 4;
  8. end
  9. properties (GetAccess = public, Constant = true)
  10. ByPlateDimensions = 1;
  11. ByWellDimensions = 3;
  12. ByElectrodeDimensions = 5;
  13. end
  14. properties (GetAccess = public, SetAccess = private)
  15. Well
  16. Electrode
  17. Timespan
  18. Dimensions
  19. end
  20. methods (Static)
  21. function this = LoadArgs(argin)
  22. % Internal support function for AxisFile Construction
  23. % loadAxISFileParseArgs Parses arguments for file readers
  24. %
  25. % Legal forms:
  26. % LoadArgs();
  27. % LoadArgs(well);
  28. % LoadArgs(electrode);
  29. % LoadArgs(well, electrode);
  30. % LoadArgs(timespan);
  31. % LoadArgs(well, timespan);
  32. % LoadArgs(electrode, timespan);
  33. % LoadArgs(well, electrode, timespan);
  34. % LoadArgs(dimensions);
  35. % LoadArgs(well, dimensions);
  36. % LoadArgs(electrode, dimensions);
  37. % LoadArgs(well, electrode, dimensions);
  38. % LoadArgs(timespan, dimensions);
  39. % LoadArgs(well, timespan, dimensions);
  40. % LoadArgs(electrode, timespan, dimensions);
  41. % LoadArgs(well, electrode, timespan, dimensions);
  42. %
  43. % Required arguments:
  44. % filename Pathname of the file to load
  45. %
  46. % Optional arguments:
  47. % well String listing which wells (in a multiwell file) to load.
  48. % Format is a comma-delimited string with whitespace ignored, e.g.
  49. % 'A1, B2,C3' limits the data loaded to wells A1, B2, and C3.
  50. % Also acceptable: 'all' to load all wells.
  51. % If this parameter is omitted, all wells are loaded.
  52. % For a single-well file, this parameter is ignored.
  53. %
  54. % electrode Which electrodes to load. Format is either a comma-delimited string
  55. % with whitespace ignored (e.g. '11, 22,33') or a single channel number;
  56. % that is, a number, not part of a string.
  57. % Also acceptable: 'all' to load all channels and 'none', '-1', or -1
  58. % to load no data (returns only header information).
  59. % If this parameter is omitted, all channels are loaded.
  60. %
  61. % timespan Span of time, in seconds, over which to load data. Format is a two-element
  62. % array, [t0 t1], where t0 is the start time and t1 is the end time and both
  63. % are in seconds after the first sample in the file. Samples returned are ones
  64. % that were taken at time >= t0 and <= t1. The beginning of the file
  65. % is at 0 seconds.
  66. % If this parameter is omitted, the data is not filtered based on time.
  67. %
  68. % dimensions Preferred number of dimensions to report the waveforms in.
  69. % Value must be a whole number scalar, and only certain values are allowed:
  70. %
  71. % dimensions = 1 -> ByPlate: returns a vector of Waveform objects, 1 Waveform
  72. % s per signal in the plate
  73. % dimensions = 3 -> ByWell: Cell Array of vectors of waveform 1 Waveform per signal
  74. % in the electrode with size (well Rows) x (well Columns)
  75. % dimensions = 5 -> ByElectrode: Cell Array of vectors of waveform 1 Waveform per .
  76. % signal in the electrode with size (well Rows) x (well Columns) x
  77. % (electrode Columns) x (electrode Rows)
  78. this.Well = [];
  79. this.Electrode = [];
  80. this.Timespan = [];
  81. this.Dimensions = [];
  82. fLastArg = [];
  83. fNumArgs = length(argin);
  84. if fNumArgs > 4
  85. error('LoadArgs:excessArgs', 'Too many arguments specified');
  86. end
  87. for i = 1:fNumArgs
  88. fCurrentArg = argin{i};
  89. if isempty(fCurrentArg) %ignore empty args
  90. continue
  91. end
  92. if isempty(fLastArg)
  93. fParseAsWell = LoadArgs.canonical_well_electrode_argument(fCurrentArg, LoadArgs.wellVal);
  94. if ~isempty(fParseAsWell)
  95. % Argument is a well
  96. this.Well = fParseAsWell;
  97. fLastArg = LoadArgs.wellVal;
  98. continue;
  99. end
  100. end
  101. if isempty(fLastArg) || fLastArg == LoadArgs.wellVal
  102. fParseAsElectrode = LoadArgs.canonical_well_electrode_argument(fCurrentArg, LoadArgs.electrodeVal);
  103. if ~isempty(fParseAsElectrode)
  104. % Argument is an electrode
  105. this.Electrode = fParseAsElectrode;
  106. fLastArg = LoadArgs.electrodeVal;
  107. continue;
  108. end
  109. end
  110. if isempty(fLastArg) || fLastArg == LoadArgs.wellVal || fLastArg == LoadArgs.electrodeVal
  111. fParseAsTimespan = LoadArgs.canonical_timespan_argument(fCurrentArg);
  112. if ~isempty(fParseAsTimespan)
  113. % Argument is a timespanVal
  114. if isnumeric(fParseAsTimespan) && fParseAsTimespan(2) < fParseAsTimespan(1)
  115. error('load_AxIS_file_parse_args:invalidTimespan', 'Invalid timespan argument: t1 < t0');
  116. end
  117. this.Timespan = fParseAsTimespan;
  118. fLastArg = LoadArgs.timespanVal;
  119. continue;
  120. end
  121. end
  122. if isempty(fLastArg) || i == length(argin)
  123. if isscalar(fCurrentArg)
  124. switch fCurrentArg
  125. case { LoadArgs.ByPlateDimensions, ...
  126. LoadArgs.ByWellDimensions, ...
  127. LoadArgs.ByElectrodeDimensions}
  128. this.Dimensions = fCurrentArg;
  129. otherwise
  130. this.Dimensions = [];
  131. end
  132. fLastArg = LoadArgs.dimensionsVal;
  133. continue;
  134. end
  135. end
  136. % If we get here, the argument couldn't be parsed
  137. error('load_AxIS_file_parse_args:invalidArg', ['Invalid argument #' num2str(i+1) ' to load_AxIS_file']);
  138. end
  139. if isempty(this.Well)
  140. % Default: all wells
  141. this.Well = 'all';
  142. end
  143. if isempty(this.Electrode)
  144. % Default: all electrodes
  145. this.Electrode = 'all';
  146. end
  147. if isempty(this.Timespan)
  148. % Default: all time
  149. this.Timespan = 'all';
  150. end
  151. end
  152. end
  153. methods(Static, Access = private)
  154. function aParseOutput = canonical_well_electrode_argument(aArgument, type)
  155. DELIMITER = ',';
  156. aParseOutput = [];
  157. % Error-check argument type
  158. if isa(aArgument, 'LoadArgs') && ~(LoadArgs.wellVal || LoadArgs.electrodeVal)
  159. error('canonical_well_electrode_argument:invalidArgType', 'Internal error: Invalid argument type for parsing');
  160. end
  161. % Special cases
  162. if strcmpi(aArgument, 'all')
  163. aParseOutput = 'all';
  164. elseif type == LoadArgs.electrodeVal && isscalar(aArgument) && aArgument == -1
  165. aParseOutput = 'none';
  166. elseif type == LoadArgs.electrodeVal && isscalar(aArgument) && isnumeric(aArgument) && aArgument > 10
  167. aParseOutput = [floor(aArgument/10) mod(aArgument, 10) ];
  168. elseif ischar(aArgument)
  169. if type == LoadArgs.electrodeVal && ( strcmp(strtrim(aArgument), '-1') || strcmpi(aArgument, 'none') )
  170. aParseOutput = 'none';
  171. return;
  172. end
  173. % Convert well names to upper case
  174. fCanonicalArg = upper(aArgument);
  175. % Strip whitespace
  176. fCanonicalArg(isspace(fCanonicalArg)) = [];
  177. % Is it valid?
  178. while ~isempty(fCanonicalArg)
  179. if length(fCanonicalArg) >= 2 && ...
  180. ( (type == LoadArgs.wellVal) && isletter(fCanonicalArg(1)) && LoadArgs.isdigit_ax(fCanonicalArg(2))) || ...
  181. ( (type == LoadArgs.electrodeVal) && LoadArgs.isdigit_ax(fCanonicalArg(1)) && LoadArgs.isdigit_ax(fCanonicalArg(2)))
  182. % Valid format
  183. if type == LoadArgs.wellVal
  184. % Format is Letter then Number, where letter is the row and number is the column
  185. % Build array of column, row
  186. [nextWell, fCanonicalArg] = strtok(fCanonicalArg, ',');
  187. aParseOutput = [ aParseOutput ; ...
  188. str2num(nextWell(2:end)) (char(nextWell(1)) - char('A') + 1)];
  189. else
  190. % Format is Number then Number, where the first is the column and the second is the row
  191. % Build array of column, row
  192. [nextWell, fCanonicalArg] = strtok(fCanonicalArg, ',');
  193. aParseOutput = [ aParseOutput ; ...
  194. str2num(nextWell(1)) str2num(nextWell(2:end))];
  195. end
  196. % Look for the next delimiter
  197. if length(fCanonicalArg) >= 1
  198. if fCanonicalArg(1) == DELIMITER
  199. fCanonicalArg = fCanonicalArg(2:end);
  200. else
  201. % Invalid next character - not a delimiter
  202. aParseOutput = [];
  203. break;
  204. end
  205. end
  206. else
  207. % Invalid Well ID
  208. aParseOutput = [];
  209. break;
  210. end
  211. end
  212. end
  213. end
  214. function aParseOutput = canonical_timespan_argument(aArgument)
  215. if isvector(aArgument) && length(aArgument) == 2 && isnumeric(aArgument)
  216. aParseOutput = aArgument(:);
  217. else
  218. aParseOutput = [];
  219. end
  220. end
  221. function t = isdigit_ax(c)
  222. %ISDIGIT True for decimal digits.
  223. %
  224. % For a string C, ISDIGIT(C) is 1 for decimal digits and 0 otherwise.
  225. %
  226. narginchk(1, 1);
  227. t = ischar(c) & ( '0' <= c ) & ( c <= '9' );
  228. end
  229. end
  230. end