Skip to content

Commit

Permalink
Merge pull request #2176 from AllenInstitute/bugfix/2176-rework-analy…
Browse files Browse the repository at this point in the history
…sis-parameter-checking

Unify and enhance analysis parameter checking
  • Loading branch information
t-b authored Jul 17, 2024
2 parents 8e5ad30 + bcb6c1d commit ff1cdee
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 83 deletions.
88 changes: 78 additions & 10 deletions Packages/MIES/MIES_AnalysisFunctionHelpers.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -837,7 +837,15 @@ Function/S AFH_RemoveAnalysisParameter(name, params)
return GrepList(params, "(?i)\\Q" + name + "\\E" + ":.*", 1, ",")
End

/// @brief Check the analysis parameters according to the optionally present check function
/// @brief Check the analysis parameters
///
/// The following checks are made:
/// - Valid and matching types
/// - Non-empty waves supplied
/// - All required parameters are present
/// - No additional parameters are present if some are required/optional
/// - Valid names
/// - Check function passes if present
///
/// @param genericFunc Name of an analysis V3 function
/// @param s struct CheckParametersStruct with additional info
Expand All @@ -847,25 +855,23 @@ Function/S AFH_CheckAnalysisParameter(string genericFunc, STRUCT CheckParameters
string allNames, presentNames, message, name
string reqNamesAndTypesFromFunc, reqNames
string optNamesAndTypesFromFunc, optNames
variable index, numParams, i, valid_f1, valid_f2
string header, text
variable index, numParams, i, valid_f1, valid_f2, isOpt, isReq, hasParamsInfo
string header, text, namesAndTypesFromFunc, reqType, suppType

FUNCREF AFP_PARAM_CHECK_V1 f1 = $(genericFunc + "_CheckParam")
FUNCREF AFP_PARAM_CHECK_V2 f2 = $(genericFunc + "_CheckParam")

valid_f1 = FuncRefIsAssigned(FuncRefInfo(f1))
valid_f2 = FuncRefIsAssigned(FuncRefInfo(f2))

if(!valid_f1 && !valid_f2)
return ""
endif

reqNamesAndTypesFromFunc = AFH_GetListOfAnalysisParams(genericFunc, REQUIRED_PARAMS)
reqNames = AFH_GetListOfAnalysisParamNames(reqNamesAndTypesFromFunc)

optNamesAndTypesFromFunc = AFH_GetListOfAnalysisParams(genericFunc, OPTIONAL_PARAMS)
optNames = AFH_GetListOfAnalysisParamNames(optNamesAndTypesFromFunc)

hasParamsInfo = !IsEmpty(optNames) || !IsEmpty(reqNames)

presentNames = AFH_GetListOfAnalysisParamNames(s.params)

allNames = GetUniqueTextEntriesFromList(optNames + reqNames + presentNames)
Expand All @@ -876,17 +882,78 @@ Function/S AFH_CheckAnalysisParameter(string genericFunc, STRUCT CheckParameters
for(i = 0; i < numParams; i += 1)
name = StringFromList(i, allNames)

if(!AFH_IsValidAnalysisParameter(name))
errorMessages[index++] = name + ": has an invalid name."
continue
endif

isOpt = (WhichListItem(name, optNames, ";", 0, 0) != -1)
isReq = (WhichListItem(name, reqNames, ";", 0, 0) != -1)

if(WhichListItem(name, presentNames, ";", 0, 0) == -1)
if(WhichListItem(name, optNames, ";", 0, 0) != -1)
if(isOpt)
// non present optional parameters should not be checked
continue
endif

// non present required parameters are an error
if(WhichListItem(name, reqNames, ";", 0, 0) != -1)
if(isReq)
errorMessages[index++] = name + ": is required but missing"
continue
endif
else
// present parameter is neither a required nor an optional parameter
if(!isOpt && !isReq && hasParamsInfo)
errorMessages[index++] = name + ": is present but neither required nor optional."
continue
endif
endif

if(hasParamsInfo)
if(isReq)
namesAndTypesFromFunc = reqNamesAndTypesFromFunc
elseif(isOpt)
namesAndTypesFromFunc = optNamesAndTypesFromFunc
else
ASSERT(0, "Invalid case")
endif

// empty type specifications are allowed
reqType = AFH_GetAnalysisParamType(name, namesAndTypesFromFunc, typeCheck = 0)
if(!IsEmpty(reqType))
// invalid types are not allowed
if(WhichListItem(reqType, ANALYSIS_FUNCTION_PARAMS_TYPES) == -1)
errorMessages[index++] = name + ": has an invalid type."
continue
endif

// non-matching type
suppType = AFH_GetAnalysisParamType(name, s.params, typeCheck = 0)
if(cmpstr(reqType, suppType))
errorMessages[index++] = name + ": has an differing types (" + reqType + " vs " + suppType + ")."
continue
endif

strswitch(reqType)
case "wave":
WAVE/Z wv = AFH_GetAnalysisParamWave(name, s.params)
if(!WaveExists(wv) || DimSize(wv, ROWS) == 0)
errorMessages[index++] = name + ": has a non-existing or empty numeric wave."
continue
endif
break
case "textwave":
WAVE/Z wv = AFH_GetAnalysisParamTextWave(name, s.params)
if(!WaveExists(wv) || DimSize(wv, ROWS) == 0)
errorMessages[index++] = name + ": has a non-existing or empty text wave."
continue
endif
break
default:
// do nothing
break
endswitch
endif
endif

AssertOnAndClearRTError()
Expand All @@ -896,7 +963,8 @@ Function/S AFH_CheckAnalysisParameter(string genericFunc, STRUCT CheckParameters
elseif(valid_f2)
message = f2(name, s); AbortOnRTE
else
ASSERT(0, "impossible case")
// no check function present
message = ""
endif

// allow null return string meaning no error
Expand Down
2 changes: 1 addition & 1 deletion Packages/MIES/MIES_AnalysisFunctions.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,7 @@ End

/// @brief Return a list of required parameters
Function/S ReachTargetVoltage_GetParams()
return "[EnableIndexing:variable],[IndexingEndStimsetAllIC:variable]"
return "[EnableIndexing:variable],[IndexingEndStimsetAllIC:string]"
End

Function/S ReachTargetVoltage_GetHelp(string name)
Expand Down
75 changes: 3 additions & 72 deletions Packages/MIES/MIES_DAEphys.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -2787,8 +2787,8 @@ static Function DAP_CheckAnalysisFunctionAndParameter(device, setName)
string device, setName

string func, listOfAnalysisFunctions
string info, str, suppParams, suppName, suppType, reqNamesAndTypesFromFunc, reqNames, reqName
string diff, name, suppNames, reqType, errorMessage
string info, str
string diff, name, errorMessage
variable i, j, numEntries

if(!CmpStr(setName, STIMSET_TP_WHILE_DAQ))
Expand Down Expand Up @@ -2845,77 +2845,8 @@ static Function DAP_CheckAnalysisFunctionAndParameter(device, setName)
continue
endif

// check that all required user parameters are supplied
// an empty list of required names is okay, this is the case when the
// names are not known beforehand (i.e. dynamic)
reqNamesAndTypesFromFunc = AFH_GetListOfAnalysisParams(func, REQUIRED_PARAMS)

reqNames = AFH_GetListOfAnalysisParamNames(reqNamesAndTypesFromFunc)
suppParams = WB_ExtractAnalysisFunctionParams(stimSet)
suppNames = AFH_GetListOfAnalysisParamNames(suppParams)
diff = GetListDifference(reqNames, suppNames, caseSensitive = 0)
if(!IsEmpty(diff))
printf "(%s) The required analysis parameters requested by %s for stim set %s were not all supplied (missing are: %s)\r", device, func, setName, diff
ControlWindowToFront()
return 1
endif

numEntries = ItemsInList(reqNames)
for(j = 0; j < numEntries; j += 1)
reqName = StringFromList(j, reqNames)

if(!AFH_IsValidAnalysisParameter(reqName))
printf "(%s) The required analysis parameter %s for %s in stim set %s has the invalid name %s.\r", device, name, func, setName, reqName
ControlWindowToFront()
return 1
endif

reqType = AFH_GetAnalysisParamType(reqName, reqNamesAndTypesFromFunc, typeCheck = 0)
// no type specification is allowed
if(IsEmpty(reqType))
continue
endif

// invalid types are not allowed
if(WhichListItem(reqType, ANALYSIS_FUNCTION_PARAMS_TYPES, ";", 0, 0) == -1)
printf "(%s) The required analysis parameter %s for %s in stim set %s has type %s which is unknown.\r", device, reqName, func, setName, reqType
ControlWindowToFront()
return 1
endif

// non matching type
suppType = AFH_GetAnalysisParamType(reqName, suppParams, typeCheck = 0)
if(cmpstr(reqType, suppType))
printf "(%s) The analysis parameter %s for %s in stim set %s has type %s but the required type is %s.\r", device, reqName, func, setName, suppType, reqType
ControlWindowToFront()
return 1
endif

strswitch(reqType)
case "wave":
WAVE/Z wv = AFH_GetAnalysisParamWave(reqName, suppParams)
if(!WaveExists(wv) || DimSize(wv, ROWS) == 0)
printf "(%s) The analysis parameter %s for %s in stim set %s is a non-existing or empty numeric wave.\r", device, reqName, func, setName
ControlWindowToFront()
return 1
endif
break
case "textwave":
WAVE/Z wv = AFH_GetAnalysisParamTextWave(reqName, suppParams)
if(!WaveExists(wv) || DimSize(wv, ROWS) == 0)
printf "(%s) The analysis parameter %s for %s in stim set %s is a non-existing or empty text wave.\r", device, reqName, func, setName
ControlWindowToFront()
return 1
endif
break
default:
// do nothing
break
endswitch
endfor

STRUCT CheckParametersStruct s
s.params = suppParams
s.params = WB_ExtractAnalysisFunctionParams(stimSet)
s.setName = setName

errorMessage = AFH_CheckAnalysisParameter(func, s)
Expand Down
30 changes: 30 additions & 0 deletions Packages/tests/HardwareBasic/UTF_AnalysisFunctionManagement.ipf
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,36 @@ static Function AFT14i([str])
endtry
End

static Function AFT14j_PreInit(device)
string device

string stimSet = "AnaFuncParams1_DA_0"
AFH_AddAnalysisParameter(stimSet, "MyVar", str = "abcd")
AFH_AddAnalysisParameter(stimSet, "MyStr", str = "abcd")
AFH_AddAnalysisParameter(stimSet, "MyWave", wv = {1, 2, 3})
Make/FREE/T textData = {"a", "b", "c"}
AFH_AddAnalysisParameter(stimSet, "MyTextWave", wv = textData)

AFH_AddAnalysisParameter(stimSet, "UnknownVar", var = 0)
End

// parameter MyVar is present but neither required nor optional and we have a _GetParams function
// UTF_TD_GENERATOR DeviceNameGeneratorMD1
static Function AFT14j([str])
string str

STRUCT DAQSettings s
InitDAQSettingsFromString(s, "MD1_RA0_I0_L0_BKG1_FAR0" + \
"__HS0_DA0_AD0_CM:IC:_ST:AnaFuncParams1_DA_0:")

try
AcquireData_NG(s, str)
FAIL()
catch
PASS()
endtry
End

// MD: mid sweep event is also called for very short stimsets
// UTF_TD_GENERATOR DeviceNameGeneratorMD1
static Function AFT15([str])
Expand Down

0 comments on commit ff1cdee

Please sign in to comment.