diff --git a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf index 045a573f2e..f2e5e60690 100644 --- a/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf +++ b/Packages/MIES/MIES_AnalysisBrowser_SweepBrowser.ipf @@ -74,7 +74,7 @@ Function/DF SB_GetSweepDataFolder(WAVE/T sweepMap, [variable sweepNo, variable i return GetAnalysisSweepPath(dataFolder, device) End -static Function/DF SB_GetSweepDataPathFromIndex(sweepBrowserDFR, mapIndex) +Function/DF SB_GetSweepDataPathFromIndex(sweepBrowserDFR, mapIndex) DFREF sweepBrowserDFR variable mapIndex @@ -722,26 +722,3 @@ Function/WAVE SB_GetSweepMap(string win) return sweepMap End - -Function [WAVE/Z numericalValues, WAVE/Z textualValues] SB_GetLabNotebooks(WAVE/T sweepMap, variable mapIndex) - - string dataFolder, device - - dataFolder = sweepMap[mapIndex][%DataFolder] - device = sweepMap[mapIndex][%Device] - WAVE/Z numericalValues = GetAnalysLBNumericalValues(dataFolder, device) - WAVE/Z textualValues = GetAnalysLBTextualValues(dataFolder, device) - - return [numericalValues, textualValues] -End - -Function/DF SB_GetSweepDF(string win, variable mapIndex) - - ASSERT(BSP_IsSweepBrowser(win), "Requires window to be a SweepBrowser") - - WAVE/T sweepMap = SB_GetSweepMap(win) - DFREF deviceDFR = SB_GetSweepDataFolder(sweepMap, index = mapIndex) - DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, str2num(sweepMap[mapIndex][%Sweep])) - - return sweepDFR -End diff --git a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf index 194f382fdf..eafa6bea30 100644 --- a/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf +++ b/Packages/MIES/MIES_AnalysisFunctionHelpers.ipf @@ -1024,6 +1024,55 @@ Function AFH_AddAnalysisParameter(string setName, string name, [variable var, st endif End +/// @brief Add an analysis parameter to the given `params` string +/// +/// Exactly one of `var`/`str`/`wv` must be given. +/// +/// @param[in, out] params stimset name +/// @param[in] name name of the parameter +/// @param[in] var [optional] numeric parameter +/// @param[in] str [optional] string parameter +/// @param[in] wv [optional] wave parameter can be numeric or text +Function AFH_AddAnalysisParameterToParams(string ¶ms, string name, [variable var, string str, WAVE wv]) + + string type, value + + ASSERT(ParamIsDefault(var) + ParamIsDefault(str) + ParamIsDefault(wv) == 2, "Expected one of var, str or wv") + + if(!ParamIsDefault(var)) + type = "variable" + // numbers never need URL encoding + value = num2str(var) + elseif(!ParamIsDefault(str)) + type = "string" + value = URLEncode(str) + elseif(!ParamIsDefault(wv)) + ASSERT(DimSize(wv, ROWS) > 0, "Expected non-empty wave") + if(IsTextWave(wv)) + type = "textwave" + Duplicate/T/FREE wv, wvText + wvText = UrlEncode(wvText) + value = TextWaveToList(wvText, "|") + else + type = "wave" + // numbers never need URL encoding + value = NumericWaveToList(wv, "|", format = "%.15g") + endif + endif + + ASSERT(AFH_IsValidAnalysisParameter(name), "Name is not a legal non-liberal igor object name") + ASSERT(!GrepString(value, "[=:,;]+"), "Broken URL encoding. Written entry contains invalid characters (one of `=:,;`)") + ASSERT(AFH_IsValidAnalysisParamType(type), "Invalid type") + +#ifndef AUTOMATED_TESTING + if(WhichListItem(name, AFH_GetListOfAnalysisParamNames(params)) != -1) + printf "Parameter \"%s\" is already present and will be overwritten!\r", name + endif +#endif + + params = ReplaceStringByKey(name, params, type + "=" + value, ":", ",", 0) +End + /// @brief Return a stringified version of the analysis parameter value /// /// @param name name of the parameter diff --git a/Packages/MIES/MIES_Constants.ipf b/Packages/MIES/MIES_Constants.ipf index 31e1352d52..54f64ff052 100644 --- a/Packages/MIES/MIES_Constants.ipf +++ b/Packages/MIES/MIES_Constants.ipf @@ -2050,11 +2050,14 @@ StrConstant SF_META_AVERAGED_FIRST_SWEEP = "/AveragedFirstSweep" // number StrConstant SF_META_XVALUES = "/XValues" // numeric wave StrConstant SF_META_XTICKLABELS = "/XTickLabels" // text wave StrConstant SF_META_XTICKPOSITIONS = "/XTickPositions" // numeric wave +StrConstant SF_META_YTICKLABELS = "/YTickLabels" // text wave +StrConstant SF_META_YTICKPOSITIONS = "/YTickPositions" // numeric wave StrConstant SF_META_XAXISLABEL = "/XAxisLabel" // string StrConstant SF_META_YAXISLABEL = "/YAxisLabel" // string StrConstant SF_META_LEGEND_LINE_PREFIX = "/LegendLinePrefix" // string +StrConstant SF_META_TAG_TEXT = "/TagText" // string StrConstant SF_META_OPSTACK = "/OperationStack" // string -StrConstant SF_META_MOD_MARKER = "/Marker" // numeric wave +StrConstant SF_META_MOD_MARKER = "/Marker" // numeric wave (per point) or number (all points) StrConstant SF_META_SHOW_LEGEND = "/ShowLegend" // numeric, boolean, defaults to true (1) StrConstant SF_META_CUSTOM_LEGEND = "/CustomLegend" // string with custom legend text, honours /ShowLegend StrConstant SF_META_ARGSETUPSTACK = "/ArgSetupStack" // string @@ -2063,9 +2066,18 @@ StrConstant SF_META_LINESTYLE = "/LineStyle" // number StrConstant SF_META_TRACE_MODE = "/TraceMode" // number StrConstant SF_META_TRACETOFRONT = "/TraceToFront" // number, boolean, defaults to false (0) StrConstant SF_META_DONOTPLOT = "/DoNotPlot" // number, boolean, defaults to false (0) +StrConstant SF_META_WINDOW_HOOK = "/WindowHook" // string + +/// A color group allows to have matching colors for sweep data with the same channel type/number and sweep. +/// It is applied before the matching headstage/average colors in #SF_GetTraceColor(). +/// +/// To use the feature set SF_META_COLOR_GROUP for traces to a unique integer, +/// see GetUniqueInteger(). All traces with the same integer are considered to +/// be in the same group. A list of supported operations is in #SF_GetTraceColor(). +StrConstant SF_META_COLOR_GROUP = "/ColorGroup" // number + +StrConstant SF_META_USER_GROUP = "/User/" // custom metadata for individual operations, top-level group with individual entries -StrConstant SF_META_USER_GROUP = "/User/" // custom metadata for individual operations, -// top-level group with individual entries StrConstant SF_META_FIT_COEFF = "FitCoefficients" StrConstant SF_META_FIT_SIGMA = "FitSigma" StrConstant SF_META_FIT_PARAMETER = "FitParameter" @@ -2111,6 +2123,7 @@ StrConstant SF_DATATYPE_SELECTSETCYCLECOUNT = "SelectSetCycleCount" StrConstant SF_DATATYPE_SELECTSETSWEEPCOUNT = "SelectSetSweepCount" StrConstant SF_DATATYPE_SELECTSCIINDEX = "SelectSCIIndex" StrConstant SF_DATATYPE_SELECTRACINDEX = "SelectRACIndex" +StrConstant SF_DATATYPE_ANAFUNCPARAM = "AnaFunc" StrConstant SF_WREF_MARKER = "\"WREF@\":" StrConstant SF_VARIABLE_MARKER = "/SF_IsVariable" // numeric diff --git a/Packages/MIES/MIES_GuiUtilities.ipf b/Packages/MIES/MIES_GuiUtilities.ipf index fe7c001c6f..483332384b 100644 --- a/Packages/MIES/MIES_GuiUtilities.ipf +++ b/Packages/MIES/MIES_GuiUtilities.ipf @@ -1390,6 +1390,26 @@ Function RestoreAnnotationPositions(string graph, WAVE/T annoInfo) endfor End +/// @brief Remove the annotations given by the `regexp` from annoInfo and return the filtered wave +Function/WAVE FilterAnnotations(WAVE/T annoInfo, string regexp) + + variable i, numEntries + string name + + Duplicate/FREE/T annoInfo, annoInfoResult + WaveClear annoInfo + + numEntries = DimSize(annoInfoResult, ROWS) + for(i = numEntries - 1; i >= 0; i -= 1) + name = GetDimLabel(annoInfoResult, ROWS, i) + if(GrepString(name, regexp)) + DeletePoints/M=(ROWS) i, 1, annoInfoResult + endif + endfor + + return annoInfoResult +End + /// @brief Autoscale all vertical axes in the visible x range Function AutoscaleVertAxisVisXRange(graph) string graph diff --git a/Packages/MIES/MIES_MiesUtilities_Logbook.ipf b/Packages/MIES/MIES_MiesUtilities_Logbook.ipf index 5921212fdc..4fee614532 100644 --- a/Packages/MIES/MIES_MiesUtilities_Logbook.ipf +++ b/Packages/MIES/MIES_MiesUtilities_Logbook.ipf @@ -2017,3 +2017,19 @@ Function [variable type, variable waMode, variable headstage] GetAnalysisFunctio return [type, waMode, headstage] End + +Function ParseLogbookMode(string modeText) + + strswitch(modeText) + case "UNKNOWN_MODE": + return UNKNOWN_MODE + case "DATA_ACQUISITION_MODE": + return DATA_ACQUISITION_MODE + case "TEST_PULSE_MODE": + return TEST_PULSE_MODE + case "NUMBER_OF_LBN_DAQ_MODES": + return NUMBER_OF_LBN_DAQ_MODES + endswitch + + ASSERT(0, "Unsupported labnotebook mode") +End diff --git a/Packages/MIES/MIES_SweepFormula.ipf b/Packages/MIES/MIES_SweepFormula.ipf index 83cd595d96..081d0d4614 100644 --- a/Packages/MIES/MIES_SweepFormula.ipf +++ b/Packages/MIES/MIES_SweepFormula.ipf @@ -88,6 +88,7 @@ static StrConstant SF_OP_BUTTERWORTH = "butterworth" static StrConstant SF_OP_SELECTCHANNELS = "selchannels" static StrConstant SF_OP_DATA = "data" static StrConstant SF_OP_LABNOTEBOOK = "labnotebook" +static StrConstant SF_OP_ANAFUNCPARAM = "anaFuncParam" static StrConstant SF_OP_WAVE = "wave" static StrConstant SF_OP_FINDLEVEL = "findlevel" static StrConstant SF_OP_EPOCHS = "epochs" @@ -250,7 +251,7 @@ Function/WAVE SF_GetNamedOperations() SF_OP_MERGE, SF_OP_FIT, SF_OP_FITLINE, SF_OP_DATASET, SF_OP_SELECTVIS, SF_OP_SELECTCM, SF_OP_SELECTSTIMSET, \ SF_OP_SELECTIVSCCSWEEPQC, SF_OP_SELECTIVSCCSETQC, SF_OP_SELECTRANGE, SF_OP_SELECTEXP, SF_OP_SELECTDEV, \ SF_OP_SELECTEXPANDSCI, SF_OP_SELECTEXPANDRAC, SF_OP_SELECTSETCYCLECOUNT, SF_OP_SELECTSETSWEEPCOUNT, \ - SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX} + SF_OP_SELECTSCIINDEX, SF_OP_SELECTRACINDEX, SF_OP_ANAFUNCPARAM} return wt End @@ -1098,6 +1099,9 @@ static Function/WAVE SF_FormulaExecutor(string graph, variable jsonID, [string j case SF_OP_LABNOTEBOOK: WAVE out = SF_OperationLabnotebook(jsonId, jsonPath, graph) break + case SF_OP_ANAFUNCPARAM: + WAVE out = SF_OperationAnaFuncParam(jsonId, jsonPath, graph) + break case SF_OP_LOG: // JSON logic debug operation WAVE out = SF_OperationLog(jsonId, jsonPath, graph) break @@ -1313,6 +1317,8 @@ static Function/S SF_GetAnnotationPrefix(string dataType) return "TP " case SF_DATATYPE_LABNOTEBOOK: return "LB " + case SF_DATATYPE_ANAFUNCPARAM: + return "AFP " default: ASSERT(0, "Invalid dataType") endswitch @@ -1330,6 +1336,7 @@ static Function/S SF_GetTraceAnnotationText(STRUCT SF_PlotMetaData &plotMetaData case SF_DATATYPE_EPOCHS: // fallthrough case SF_DATATYPE_SWEEP: // fallthrough case SF_DATATYPE_LABNOTEBOOK: // fallthrough + case SF_DATATYPE_ANAFUNCPARAM: // fallthrough case SF_DATATYPE_TP: sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) legendPrefix = JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX) @@ -1383,16 +1390,103 @@ static Function/S SF_GetMetaDataAnnotationText(STRUCT SF_PlotMetaData &plotMetaD return "\\s(" + traceName + ") " + SF_GetTraceAnnotationText(plotMetaData, data) + "\r" End -Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE data) +static Function/WAVE SF_GenerateTraceColors(WAVE colorGroups) + + variable numUniqueColors, i + string lbl + + WAVE uniqueColorGroups = GetUniqueEntries(colorGroups) + numUniqueColors = DimSize(uniqueColorGroups, ROWS) + WAVE traceColors = GetColorWave(numUniqueColors) + + for(i = 0; i < numUniqueColors; i += 1) + [STRUCT RGBColor s] = GetTraceColorAlternative(i) + + lbl = num2str(uniqueColorGroups[i]) + SetDimLabel ROWS, i, $lbl, traceColors + + traceColors[i][%Red] = s.red + traceColors[i][%Green] = s.green + traceColors[i][%Blue] = s.blue + endfor + + return traceColors +End + +/// @brief Return an Nx3 wave with one color triplett for each unique trace color group +static Function/WAVE SF_GetGroupColors(WAVE/WAVE formulaResults) + + variable numFormulas, i, numUniqueColors, refColorGroup, constantChannelNumAndType + string lbl + + numFormulas = DimSize(formulaResults, ROWS) + + if(numFormulas == 0) + return $"" + endif + + WAVE/Z data = formulaResults[0][%FORMULAY] + + if(!WaveExists(data)) + return $"" + endif + + refColorGroup = JWN_GetNumberFromWaveNote(data, SF_META_COLOR_GROUP) + + if(IsNaN(refColorGroup)) + return $"" + endif + + Make/FREE/N=(numFormulas)/D colorGroups = JWN_GetNumberFromWaveNote(formulaResults[p][%FORMULAY], SF_META_COLOR_GROUP) + + if(numFormulas == 1) + return SF_GenerateTraceColors(colorGroups) + endif + + // check if the data in the y formulas is from the same channel type and number + Make/FREE/N=(numFormulas) channelNumbers = JWN_GetNumberFromWaveNote(formulaResults[p][%FORMULAY], SF_META_CHANNELNUMBER) + Make/FREE/N=(numFormulas) channelTypes = JWN_GetNumberFromWaveNote(formulaResults[p][%FORMULAY], SF_META_CHANNELTYPE) + + constantChannelNumAndType = IsConstant(channelNumbers, channelNumbers[0], ignoreNaN = 0) \ + && IsConstant(channelTypes, channelTypes[0], ignoreNaN = 0) + + if(!constantChannelNumAndType) + return $"" + endif + + return SF_GenerateTraceColors(colorGroups) +End + +Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE data, WAVE/Z traceGroupColors) variable i, channelNumber, channelType, sweepNo, headstage, numDoInh, minVal, isAveraged, mapIndex + variable colorGroup, idx + + if(WaveExists(traceGroupColors)) + // Operations with trace group color support: + // - data/epochs/tp/psxKernel (via SFH_GetSweepsForFormula) + // - labnotebook + // - anaFuncParam + + colorGroup = JWN_GetNumberFromWaveNote(data, SF_META_COLOR_GROUP) + ASSERT(IsFinite(colorGroup), "Invalid color group") + + idx = FindDimLabel(traceGroupColors, ROWS, num2str(colorGroup)) + ASSERT(idx >= 0, "Invalid color group index") + + s.red = traceGroupColors[idx][%Red] + s.green = traceGroupColors[idx][%Green] + s.blue = traceGroupColors[idx][%Blue] + + return [s] + endif s.red = 0xFFFF s.green = 0x0000 s.blue = 0x0000 Make/FREE/T stopInheritance = {SF_OPSHORT_MINUS, SF_OPSHORT_PLUS, SF_OPSHORT_DIV, SF_OPSHORT_MULT} - Make/FREE/T doInheritance = {SF_OP_DATA, SF_OP_TP, SF_OP_PSX, SF_OP_PSX_STATS, SF_OP_EPOCHS, SF_OP_LABNOTEBOOK} + Make/FREE/T doInheritance = {SF_OP_DATA, SF_OP_TP, SF_OP_PSX, SF_OP_PSX_STATS, SF_OP_EPOCHS, SF_OP_LABNOTEBOOK, SF_OP_ANAFUNCPARAM} WAVE/T opStackW = ListToTextWave(opStack, ";") numDoInh = DimSize(doInheritance, ROWS) @@ -1421,7 +1515,7 @@ Function [STRUCT RGBColor s] SF_GetTraceColor(string graph, string opStack, WAVE return [s] endif - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) if(!WaveExists(numericalValues)) return [s] endif @@ -1727,11 +1821,11 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode string trace, customLegend variable i, j, k, l, numTraces, splitTraces, splitY, splitX, numGraphs, numWins, numData, dataCnt, traceCnt - variable dim1Y, dim2Y, dim1X, dim2X, winDisplayMode, showLegend + variable dim1Y, dim2Y, dim1X, dim2X, winDisplayMode, showLegend, tagCounter, overrideMarker variable xMxN, yMxN, xPoints, yPoints, keepUserSelection, numAnnotations, formulasAreDifferent, postPlotPSX variable formulaCounter, gdIndex, markerCode, lineCode, lineStyle, traceToFront, isCategoryAxis string win, wList, winNameTemplate, exWList, wName, annotation, yAxisLabel, wvName, info, xAxis - string formulasRemain, yAndXFormula, xFormula, yFormula + string formulasRemain, yAndXFormula, xFormula, yFormula, tagText, name, winHook STRUCT SF_PlotMetaData plotMetaData STRUCT RGBColor color @@ -1802,6 +1896,8 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode SF_FormulaPlotterExtendResultsIfCompatible(formulaResults) + WAVE/Z traceGroupColors = SF_GetGroupColors(formulaResults) + numData = DimSize(formulaResults, ROWS) for(k = 0; k < numData; k += 1) @@ -1816,7 +1912,7 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode SFH_ASSERT(!(IsTextWave(wvResultY) && WaveDims(wvResultY) > 1), "Plotter got 2d+ text wave as y data.") - [color] = SF_GetTraceColor(graph, plotMetaData.opStack, wvResultY) + [color] = SF_GetTraceColor(graph, plotMetaData.opStack, wvResultY, traceGroupColors) if(!WaveExists(wvResultX) && !IsEmpty(plotMetaData.xAxisLabel)) WAVE/Z wvResultX = JWN_GetNumericWaveFromWaveNote(wvResultY, SF_META_XVALUES) @@ -2029,6 +2125,45 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode endif endif + WAVE/Z xTickLabelsAsFree = JWN_GetTextWaveFromWaveNote(formulaResults, SF_META_XTICKLABELS) + WAVE/Z xTickPositionsAsFree = JWN_GetNumericWaveFromWaveNote(formulaResults, SF_META_XTICKPOSITIONS) + + if(WaveExists(xTickLabelsAsFree) && WaveExists(xTickPositionsAsFree)) + DFREF dfrWork = SFH_GetWorkingDF(graph) + wvName = "xTickLabels_" + win + "_" + NameOfWave(formulaResults) + WAVE xTickLabels = MoveFreeWaveToPermanent(xTickLabelsAsFree, dfrWork, wvName) + + wvName = "xTickPositions_" + win + "_" + NameOfWave(formulaResults) + WAVE xTickPositions = MoveFreeWaveToPermanent(xTickPositionsAsFree, dfrWork, wvName) + + ModifyGraph/Z/W=$win userticks(bottom)={xTickPositions, xTickLabels} + endif + + WAVE/Z yTickLabelsAsFree = JWN_GetTextWaveFromWaveNote(formulaResults, SF_META_YTICKLABELS) + WAVE/Z yTickPositionsAsFree = JWN_GetNumericWaveFromWaveNote(formulaResults, SF_META_YTICKPOSITIONS) + + if(WaveExists(yTickLabelsAsFree) && WaveExists(yTickPositionsAsFree)) + DFREF dfrWork = SFH_GetWorkingDF(graph) + wvName = "yTickLabels_" + win + "_" + NameOfWave(formulaResults) + WAVE yTickLabels = MoveFreeWaveToPermanent(yTickLabelsAsFree, dfrWork, wvName) + + wvName = "yTickPositions_" + win + "_" + NameOfWave(formulaResults) + WAVE yTickPositions = MoveFreeWaveToPermanent(yTickPositionsAsFree, dfrWork, wvName) + + ModifyGraph/Z/W=$win userticks(left)={yTickPositions, yTickLabels} + + Make/FREE yTickPositionsWorkaround = {0, 1} + if(EqualWaves(yTickPositions, yTickPositionsWorkaround, EQWAVES_DATA)) + // @todo workaround bug 4531 so that we get tick labels even with only constant y values + SetAxis/W=$win/Z left, 0, 1 + endif + endif + + winHook = JWN_GetStringFromWaveNote(formulaResults, SF_META_WINDOW_HOOK) + if(!IsEmpty(winHook)) + SetWindow $win, tooltipHook(SweepFormulaTraceValue)=$winHook + endif + for(k = 0; k < formulaCounter; k += 1) WAVE/WAVE plotFormData = collPlotFormData[k] WAVE/T tracesInGraph = plotFormData[0] @@ -2055,8 +2190,22 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode WAVE/Z traceColor = JWN_GetNumericWaveFromWaveNote(traceColorHolder, SF_META_TRACECOLOR) if(WaveExists(traceColor)) - ASSERT(DimSize(traceColor, ROWS) == 3, "Need 3-element wave for color specification.") - ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2]) + switch(DimSize(traceColor, ROWS)) + case 3: + ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2]) + break + case 4: + ModifyGraph/W=$win rgb($trace)=(traceColor[0], traceColor[1], traceColor[2], traceColor[3]) + break + default: + ASSERT(0, "Invalid size of trace color wave") + endswitch + endif + + tagText = JWN_GetStringFromWaveNote(wvY, SF_META_TAG_TEXT) + if(!IsEmpty(tagText)) + name = "tag" + num2str(tagCounter++) + Tag/C/N=$name/W=$win/F=0/L=0/X=0.00/Y=0.00 $trace, 0, tagText endif ModifyGraph/W=$win mode($trace)=SF_DeriveTraceDisplayMode(wvX, wvY) @@ -2076,22 +2225,13 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode ASSERT(DimSize(wvY, ROWS) == DimSize(customMarker, ROWS), "Marker size mismatch") ModifyGraph/W=$win zmrkNum($trace)={customMarker} else - ModifyGraph/W=$win marker($trace)=markerCode - endif - - WAVE/Z xTickLabelsAsFree = JWN_GetTextWaveFromWaveNote(wvY, SF_META_XTICKLABELS) - WAVE/Z xTickPositionsAsFree = JWN_GetNumericWaveFromWaveNote(wvY, SF_META_XTICKPOSITIONS) - - if(WaveExists(xTickLabelsAsFree) && WaveExists(xTickPositionsAsFree)) - DFREF dfrWork = SFH_GetWorkingDF(graph) - wvName = "xTickLabels_" + NameOfWave(wvY) - WAVE xTickLabels = MoveFreeWaveToPermanent(xTickLabelsAsFree, dfrWork, wvName) + overrideMarker = JWN_GetNumberFromWaveNote(wvY, SF_META_MOD_MARKER) - wvName = "xTickPositions_" + NameOfWave(wvY) - WAVE xTickPositions = MoveFreeWaveToPermanent(xTickPositionsAsFree, dfrWork, wvName) + if(!IsNaN(overrideMarker)) + markerCode = overrideMarker + endif - xAxis = StringByKey("XAXIS", TraceInfo(win, trace, 0)) - ModifyGraph/W=$win userticks($xAxis)={xTickPositions, xTickLabels} + ModifyGraph/W=$win marker($trace)=markerCode endif traceToFront = JWN_GetNumberFromWaveNote(wvY, SF_META_TRACETOFRONT) @@ -2133,7 +2273,8 @@ static Function SF_FormulaPlotter(string graph, string formula, [variable dmMode endif if(WaveExists(annoInfos)) - RestoreAnnotationPositions(win, annoInfos) + WAVE/T annoInfosFiltered = FilterAnnotations(annoInfos, "^tag.*$") + RestoreAnnotationPositions(win, annoInfosFiltered) endif endif endfor @@ -3038,6 +3179,8 @@ static Function/WAVE SF_OperationTP(variable jsonId, string jsonPath, string gra JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_TP) JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_TP, "")) + SF_SetSweepXAxisTickLabels(output, selectData) + return SFH_GetOutputForExecutor(output, graph, SF_OP_TP) End @@ -3475,13 +3618,15 @@ static Function/WAVE SF_OperationEpochs(variable jsonId, string jsonPath, string WAVE/WAVE output = SF_OperationEpochsImpl(graph, epochPatterns, selectData, epType, SF_OP_EPOCHS) + SF_SetSweepXAxisTickLabels(output, selectData) + return SFH_GetOutputForExecutor(output, graph, SF_OP_EPOCHS) End static Function/WAVE SF_OperationEpochsImpl(string graph, WAVE/T epochPatterns, WAVE/Z selectData, variable epType, string opShort) variable i, j, numSelected, sweepNo, chanNr, chanType, index, numEpochs, epIndex, settingsIndex, numPatterns, numEntries - variable hasValidData + variable hasValidData, colorGroup string epName, epShortName, epEntry, yAxisLabel, epAxisName ASSERT(WindowExists(graph), "graph window does not exist") @@ -3559,6 +3704,9 @@ static Function/WAVE SF_OperationEpochsImpl(string graph, WAVE/T epochPatterns, JWN_SetNumberInWaveNote(output[i], SF_META_CHANNELNUMBER, chanNr) JWN_SetWaveInWaveNote(output[i], SF_META_XVALUES, {sweepNo}) + colorGroup = GetUniqueInteger() + JWN_SetNumberInWaveNote(output[i], SF_META_COLOR_GROUP, colorGroup) + hasValidData = 1 endfor @@ -5298,8 +5446,9 @@ static Function/WAVE SF_GetSelectDataWithRACorSCIIndex(string graph, WAVE select endif for(i = 0; i < numSelected; i += 1) - sweepNo = selectData[i][%SWEEP] - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) + sweepNo = selectData[i][%SWEEP] + mapIndex = selectData[i][%SWEEPMAPINDEX] + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) ASSERT(WaveExists(numericalValues), "Could not resolve numerical LNB") if(mode == SELECTDATA_MODE_RAC) cycleIds[i] = GetLastSettingIndep(numericalValues, sweepNo, RA_ACQ_CYCLE_ID_KEY, DATA_ACQUISITION_MODE, defValue = NaN) @@ -5512,7 +5661,7 @@ static Function/WAVE SF_GetSelectDataWithSCIorRAC(string graph, WAVE selectData, for(i = 0; i < numSelected; i += 1) sweepNo = selectData[i][%SWEEP] mapIndex = selectData[i][%SWEEPMAPINDEX] - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) if(!WaveExists(numericalValues)) continue endif @@ -5660,59 +5809,213 @@ static Function/WAVE SF_OperationData(variable jsonId, string jsonPath, string g return SFH_GetOutputForExecutor(output, graph, SF_OP_DATA) End -/// `labnotebook(string key[, array selectData [, string entrySourceType]])` +static Function/WAVE SF_OperationAnaFuncParam(variable jsonId, string jsonPath, string graph) + + SFH_CheckArgumentCount(jsonID, jsonPath, SF_OP_ANAFUNCPARAM, 0, maxArgs = 2) + + WAVE/T names = SFH_GetArgumentAsWave(jsonId, jsonPath, graph, SF_OP_ANAFUNCPARAM, 0, singleResult = 1) + WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_DATA, 1) + + WAVE/WAVE output = SF_OperationAnaFuncParamIterate(graph, names, selectData, SF_OP_ANAFUNCPARAM) + + JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_ANAFUNCPARAM, "")) + JWN_SetStringInWaveNote(output, SF_META_WINDOW_HOOK, "TraceValueDisplayHook") + + SF_SetSweepXAxisTickLabels(output, selectData) + + return SFH_GetOutputForExecutor(output, graph, SF_OP_ANAFUNCPARAM) +End + +static Function/WAVE SF_OperationAnaFuncParamIterate(string graph, WAVE/T names, WAVE/WAVE/Z selectDataArray, string opShort) + + if(!WaveExists(selectDataArray)) + return $"" + endif + + WAVE/WAVE/Z result = $"" + + for(WAVE/WAVE/Z selectDataComp : selectDataArray) + + if(!WaveExists(selectDataComp)) + continue + endif + + WAVE/Z selectData = selectDataComp[%SELECTION] + WAVE/WAVE sweepData = SF_OperationAnaFuncParamImpl(graph, names, selectData, opShort) + if(!WaveExists(sweepData)) + continue + endif + + if(!WaveExists(result)) + WAVE/WAVE result = sweepData + continue + endif + + Concatenate/FREE/WAVE/NP {sweepData}, result + endfor + + return result +End + +/// Gather all requested analysis function parameters with wildcard support +/// +/// @param names requested analysis function parameter names, can include wildcards +/// @param lbnParams wave reference wave, one wave per selectData entry, with +/// the analysis function parameter data from the labnotebook including JWN +/// metadata +/// +/// @return expanded analysis function parameter names (i.e. without wildcards) which match at least in one selectData entry +static Function/WAVE SF_OperationAnaFuncParamImplAllNames(WAVE/T names, WAVE/WAVE lbnParams) + + variable i, numAvailableParams, j, numRequestedParams + string params, reqName, namesPerLBNEntry, gatheredNames + + numAvailableParams = DimSize(lbnParams, ROWS) + numRequestedParams = DimSize(names, ROWS) + + for(i = 0; i < numAvailableParams; i += 1) + WAVE/T paramsSingle = lbnParams[i] + params = JWN_GetStringFromWaveNote(paramsSingle, SF_META_TAG_TEXT) + + gatheredNames = AFH_GetListOfAnalysisParamNames(params) + + for(j = 0; j < numRequestedParams; j += 1) + reqName = names[j] + + namesPerLBNEntry = ListMatch(gatheredNames, reqName) + + if(IsEmpty(namesPerLBNEntry)) + continue + endif + + WAVE wv = ListToTextWave(namesPerLBNEntry, ";") + + Concatenate/NP=(ROWS)/FREE {wv}, allNames + endfor + endfor + + if(!WaveExists(allNames)) + return $"" + endif + + return GetUniqueEntries(allNames) +End + +static Function/WAVE SF_OperationAnaFuncParamImpl(string graph, WAVE/T names, WAVE/Z selectData, string opShort) + + variable numReqNames, numFoundParams, i, j, idx, sweepNo, chanType, chanNr, colorGroup, colorGroupFound, nextFreeIndex, marker + string params, name, type + + if(!WaveExists(selectData)) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_ANAFUNCPARAM) + return output + endif + + WAVE/WAVE allParams = SF_OperationLabnotebookImpl(graph, {ANALYSIS_FUNCTION_PARAMS_LBN}, selectData, DATA_ACQUISITION_MODE, opShort) + WAVE/T/Z allReqNames = SF_OperationAnaFuncParamImplAllNames(names, allParams) + + if(!WaveExists(allReqNames)) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_ANAFUNCPARAM) + return output + endif + + numFoundParams = DimSize(allParams, ROWS) + numReqNames = DimSize(allReqNames, ROWS) + + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numReqNames * numFoundParams) + + for(i = 0; i < numReqNames; i += 1) + name = allReqNames[i] + colorGroup = GetUniqueInteger() + + marker = SFH_GetPlotMarkerCodeSelection(i) + + for(j = 0; j < numFoundParams; j += 1) + WAVE/T paramsSingle = allParams[j] + params = JWN_GetStringFromWaveNote(paramsSingle, SF_META_TAG_TEXT) + type = AFH_GetAnalysisParamType(name, params, typeCheck = 0) + + strswitch(type) + case "variable": + Make/FREE/D out = {AFH_GetAnalysisParamNumerical(name, params)} + break + case "string": + case "wave": + case "textwave": + Make/FREE/D out = {0.0} + JWN_SetWaveInWaveNote(out, SF_META_TRACECOLOR, {0, 0, 0, 0}) + JWN_SetStringInWaveNote(out, SF_META_TAG_TEXT, PrepareListForDisplay(AFH_GetAnalysisParameterAsText(name, params))) + break + case "": + // unknown name or labnotebook entry not present + Make/FREE/D out = {NaN} + break + default: + ASSERT(0, "Unsupported parameter type: " + type) + endswitch + + sweepNo = JWN_GetNumberFromWaveNote(paramsSingle, SF_META_SWEEPNO) + chanType = JWN_GetNumberFromWaveNote(paramsSingle, SF_META_CHANNELTYPE) + chanNr = JWN_GetNumberFromWaveNote(paramsSingle, SF_META_CHANNELNUMBER) + + JWN_SetNumberInWaveNote(out, SF_META_SWEEPNO, sweepNo) + JWN_SetNumberInWaveNote(out, SF_META_CHANNELTYPE, chanType) + JWN_SetNumberInWaveNote(out, SF_META_CHANNELNUMBER, chanNr) + JWN_SetWaveInWaveNote(out, SF_META_XVALUES, {sweepNo}) + + JWN_SetStringInWaveNote(out, SF_META_LEGEND_LINE_PREFIX, name) + JWN_SetNumberInWaveNote(out, SF_META_COLOR_GROUP, colorGroup) + JWN_SetNumberInWaveNote(out, SF_META_MOD_MARKER, marker) + + output[idx] = out + idx += 1 + endfor + endfor + + Redimension/N=(idx) output + + JWN_SetStringInWaveNote(output, SF_META_YAXISLABEL, "Analysis function parameters") + JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "Sweeps") + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_ANAFUNCPARAM) + + return output +End + +/// `labnotebook(array keys[, array selectData [, string entrySourceType]])` /// /// return lab notebook @p key for all @p sweeps that belong to the channels @p channels static Function/WAVE SF_OperationLabnotebook(variable jsonId, string jsonPath, string graph) variable numArgs, mode - string lbnKey + string lbnKey, modeTxt - numArgs = SFH_GetNumberOfArguments(jsonID, jsonPath) - SFH_ASSERT(numArgs <= 3, "Maximum number of three arguments exceeded.") - SFH_ASSERT(numArgs >= 1, "At least one argument is required.") + SFH_CheckArgumentCount(jsonID, jsonPath, SF_OP_LABNOTEBOOK, 1, maxArgs = 3) - if(numArgs == 3) - WAVE/T wMode = SFH_ResolveDatasetElementFromJSON(jsonID, jsonPath, graph, SF_OP_LABNOTEBOOK, 2, checkExist = 1) - SFH_ASSERT(IsTextWave(wMode) && DimSize(wMode, ROWS) == 1 && !DimSize(wMode, COLS), "Last parameter needs to be a string.") - strswitch(wMode[0]) - case "UNKNOWN_MODE": - mode = UNKNOWN_MODE - break - case "DATA_ACQUISITION_MODE": - mode = DATA_ACQUISITION_MODE - break - case "TEST_PULSE_MODE": - mode = TEST_PULSE_MODE - break - case "NUMBER_OF_LBN_DAQ_MODES": - mode = NUMBER_OF_LBN_DAQ_MODES - break - default: - SFH_ASSERT(0, "Undefined labnotebook mode. Use one in group DataAcqModes") - endswitch - else - mode = DATA_ACQUISITION_MODE - endif + Make/FREE/T allowedValuesMode = {"UNKNOWN_MODE", "DATA_ACQUISITION_MODE", "TEST_PULSE_MODE", "NUMBER_OF_LBN_DAQ_MODES"} + modeTxt = SFH_GetArgumentAsText(jsonID, jsonPath, graph, SF_OP_LABNOTEBOOK, 2, allowedValues = allowedValuesMode, defValue = "DATA_ACQUISITION_MODE") + mode = ParseLogbookMode(modeTxt) WAVE/Z selectData = SFH_GetArgumentSelect(jsonID, jsonPath, graph, SF_OP_LABNOTEBOOK, 1) - WAVE/T wLbnKey = SFH_ResolveDatasetElementFromJSON(jsonID, jsonPath, graph, SF_OP_LABNOTEBOOK, 0, checkExist = 1) - SFH_ASSERT(IsTextWave(wLbnKey) && DimSize(wLbnKey, ROWS) == 1 && !DimSize(wLbnKey, COLS), "First parameter needs to be a string labnotebook key.") - lbnKey = wLbnKey[0] + WAVE/T lbnKeys = SFH_GetArgumentAsWave(jsonID, jsonPath, graph, SF_OP_LABNOTEBOOK, 0, expectedWaveType = IGOR_TYPE_TEXT_WAVE, singleResult = 1) - WAVE/Z/WAVE output = SF_OperationLabnotebookIterate(graph, lbnKey, selectData, mode, SF_OP_LABNOTEBOOK) + WAVE/Z/WAVE output = SF_OperationLabnotebookIterate(graph, lbnKeys, selectData, mode, SF_OP_LABNOTEBOOK) if(!WaveExists(output)) WAVE/WAVE output = SFH_CreateSFRefWave(graph, SF_OP_LABNOTEBOOK, 0) JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) endif JWN_SetStringInWaveNote(output, SF_META_OPSTACK, AddListItem(SF_OP_LABNOTEBOOK, "")) + JWN_SetStringInWaveNote(output, SF_META_WINDOW_HOOK, "TraceValueDisplayHook") + + SF_SetSweepXAxisTickLabels(output, selectData) return SFH_GetOutputForExecutor(output, graph, SF_OP_LABNOTEBOOK) End -static Function/WAVE SF_OperationLabnotebookIterate(string graph, string lbnKey, WAVE/WAVE/Z selectDataArray, variable mode, string opShort) +static Function/WAVE SF_OperationLabnotebookIterate(string graph, WAVE/T lbnKeys, WAVE/WAVE/Z selectDataArray, variable mode, string opShort) if(!WaveExists(selectDataArray)) return $"" @@ -5727,7 +6030,7 @@ static Function/WAVE SF_OperationLabnotebookIterate(string graph, string lbnKey, endif WAVE/Z selectData = selectDataComp[%SELECTION] - WAVE/WAVE lbnData = SF_OperationLabnotebookImpl(graph, lbnKey, selectData, mode, opShort) + WAVE/WAVE lbnData = SF_OperationLabnotebookImpl(graph, lbnKeys, selectData, mode, opShort) if(!WaveExists(lbnData)) continue endif @@ -5743,63 +6046,247 @@ static Function/WAVE SF_OperationLabnotebookIterate(string graph, string lbnKey, return result End -static Function/WAVE SF_OperationLabnotebookImpl(string graph, string lbnKey, WAVE/Z selectData, variable mode, string opShort) +static Function/WAVE SF_OperationLabnotebookImpl(string graph, WAVE/T LBNKeys, WAVE/Z selectData, variable mode, string opShort) - variable i, numSelected, index, settingsIndex - variable sweepNo, chanNr, chanType + variable i, numSelected, idx, lbnIndex + variable numOutputWaves, colorGroup, marker + string lbnKey, refUnit, unitString if(!WaveExists(selectData)) - return $"" + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) + return output endif - numSelected = DimSize(selectData, ROWS) - WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numSelected) + WAVE/T/Z allLBNKeys = SFH_OperationLabnotebookExpandKeys(graph, LBNKeys, selectData, mode) - for(i = 0; i < numSelected; i += 1) + if(!WaveExists(allLBNKeys)) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, 0) + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) + return output + endif - sweepNo = selectData[i][%SWEEP] - if(!IsValidSweepNumber(sweepNo)) - continue - endif - chanNr = selectData[i][%CHANNELNUMBER] - chanType = selectData[i][%CHANNELTYPE] + numSelected = DimSize(selectData, ROWS) + numOutputWaves = numSelected * DimSize(allLBNKeys, ROWS) + WAVE/WAVE output = SFH_CreateSFRefWave(graph, opShort, numOutputWaves) - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, selectData[i][%SWEEPMAPINDEX]) - if(!WaveExists(numericalValues) || !WaveExists(textualValues)) - continue + for(lbnKey : allLBNKeys) + colorGroup = GetUniqueInteger() + marker = SFH_GetPlotMarkerCodeSelection(lbnIndex) + lbnIndex += 1 + + for(i = 0; i < numSelected; i += 1) + WAVE out = SF_OperationLabnotebookImplGetEntry(graph, selectData, i, lbnKey, mode) + + JWN_SetNumberInWaveNote(out, SF_META_COLOR_GROUP, colorGroup) + JWN_SetNumberInWaveNote(out, SF_META_MOD_MARKER, marker) + + output[idx] = out + idx += 1 + endfor + endfor + + WAVE/T units = SF_GetLabnotebookEntryUnits(graph, allLBNKeys, selectData) + + if(DimSize(units, ROWS) == 1) + refUnit = units[0] + + if(!cmpstr(refUnit, LABNOTEBOOK_BINARY_UNIT)) + WAVE/T/Z matches = GrepTextWave(allLBNKeys, "^.* QC$") + + Make/FREE/D yTickPositions = {0, 1} + Make/FREE/T/N=2 yTickLabels + + if(WaveExists(matches) && DimSize(matches, ROWS) == DimSize(allLBNKeys, ROWS)) + yTickLabels[] = UpperCaseFirstChar(ToPassFail(yTickPositions[p])) + else + yTickLabels[] = UpperCaseFirstChar(ToOnOff(yTickPositions[p])) + endif + + JWN_SetWaveInWaveNote(output, SF_META_YTICKPOSITIONS, yTickPositions) + JWN_SetWaveInWaveNote(output, SF_META_YTICKLABELS, yTickLabels) + else + // other specializations on labnotebook units + + if(!IsEmpty(refUnit)) + sprintf unitString, "Unit (%s)", refUnit + JWN_SetStringInWaveNote(output, SF_META_YAXISLABEL, unitString) + endif endif + endif + + JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) + JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "Sweeps") + + SF_SetSweepXAxisTickLabels(output, selectData) + + return output +End + +static Function/WAVE SF_OperationLabnotebookImplGetEntry(string graph, WAVE selectData, variable index, string lbnKey, variable mode) + + variable sweepNo, chanNr, chanType, settingsIndex, result, col, mapIndex + string entry + + sweepNo = selectData[index][%SWEEP] + chanNr = selectData[index][%CHANNELNUMBER] + chanType = selectData[index][%CHANNELTYPE] + mapIndex = selectData[index][%SWEEPMAPINDEX] + + Make/FREE/D out = {NaN} + + JWN_SetNumberInWaveNote(out, SF_META_SWEEPNO, sweepNo) + JWN_SetNumberInWaveNote(out, SF_META_CHANNELTYPE, chanType) + JWN_SetNumberInWaveNote(out, SF_META_CHANNELNUMBER, chanNr) + JWN_SetNumberInWaveNote(out, SF_META_SWEEPMAPINDEX, mapIndex) + JWN_SetWaveInWaveNote(out, SF_META_XVALUES, {sweepNo}) + JWN_SetStringInWaveNote(out, SF_META_LEGEND_LINE_PREFIX, lbnKey) + + if(!IsValidSweepNumber(sweepNo)) + return out + endif + + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) + WAVE textualValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_TEXTUAL_VALUES) + + [WAVE settings, settingsIndex] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, lbnKey, chanNr, chanType, mode) + if(!WaveExists(settings)) + return out + endif + + if(IsNumericWave(settings)) + out[0] = {settings[settingsIndex]} + elseif(IsTextWave(settings)) + out[0] = {0.0} + WAVE/T settingsT = settings + + entry = PrepareListForDisplay(settingsT[settingsIndex]) + + JWN_SetWaveInWaveNote(out, SF_META_TRACECOLOR, {0, 0, 0, 0}) + JWN_SetStringInWaveNote(out, SF_META_TAG_TEXT, entry) + else + ASSERT(0, "Invalid type") + endif + + return out +End + +static Function/S SF_GetLabnotebookEntryUnits_Impl(WAVE numericalKeys, WAVE textualKeys, string entry) + + variable result, col + string unit + + [result, unit, col] = LBN_GetEntryProperties(numericalKeys, entry) + + if(!result) + return unit + endif - [WAVE settings, settingsIndex] = GetLastSettingChannel(numericalValues, textualValues, sweepNo, lbnKey, chanNr, chanType, mode) - if(!WaveExists(settings)) + [result, unit, col] = LBN_GetEntryProperties(textualKeys, entry) + + if(!result) + return unit + endif + + return "" +End + +static Function/WAVE SF_GetLabnotebookEntryUnits(string graph, WAVE/T allLBNKeys, WAVE selectData) + + variable sweepNo, mapIndex + + sweepNo = selectData[0][%SWEEP] + mapIndex = selectData[0][%SWEEPMAPINDEX] + + WAVE numericalKeys = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_KEYS) + WAVE textualKeys = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_TEXTUAL_KEYS) + + Make/FREE/T/N=(DimSize(allLBNKeys, ROWS)) units = SF_GetLabnotebookEntryUnits_Impl(numericalKeys, textualKeys, allLBNKeys[p]) + + WAVE/T/Z unitsUnique = GetUniqueEntries(units) + + return unitsUnique +End + +static Function/WAVE SFH_OperationLabnotebookExpandKeys(string graph, WAVE/T LBNKeys, WAVE selectData, variable mode) + + variable i, j, numSelected, numKeys, sweepNo + string key + + numKeys = DimSize(LBNKeys, ROWS) + + Make/FREE/N=(numKeys) hasWC = HasWildcardSyntax(LBNKeys[p]) + + if(IsConstant(hasWC, 0)) + return LBNKeys + endif + + numSelected = DimSize(selectData, ROWS) + for(i = 0; i < numSelected; i += 1) + sweepNo = selectData[i][%SWEEP] + + WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) + WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) + + WAVE/T/Z entries = LBV_GetAllLogbookParamNames(textualValues, numericalValues) + + if(!WaveExists(entries)) continue endif - if(IsNumericWave(settings)) - Make/FREE/D outD = {settings[settingsIndex]} - WAVE out = outD - elseif(IsTextWave(settings)) - WAVE/T settingsT = settings - Make/FREE/T outT = {settingsT[settingsIndex]} - WAVE out = outT - endif - JWN_SetNumberInWaveNote(out, SF_META_SWEEPNO, sweepNo) - JWN_SetNumberInWaveNote(out, SF_META_CHANNELTYPE, chanType) - JWN_SetNumberInWaveNote(out, SF_META_CHANNELNUMBER, chanNr) - JWN_SetWaveInWaveNote(out, SF_META_XVALUES, {sweepNo}) + Duplicate/FREE/T entries, filteredEntries + + for(j = 0; j < numKeys; j += 1) + key = LBNKeys[j] + MultiThread filteredEntries = SelectString(stringmatch(entries[p], key), "", entries[p]) + endfor - output[index] = out - index += 1 + RemoveTextWaveEntry1D(filteredEntries, "", all = 1) + + Concatenate/NP=(ROWS)/T/FREE {filteredEntries}, allLBNKeys endfor - if(!index) + + if(DimSize(allLBNKeys, ROWS) == 0) return $"" endif - Redimension/N=(index) output - JWN_SetStringInWaveNote(output, SF_META_DATATYPE, SF_DATATYPE_LABNOTEBOOK) - JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "Sweeps") - JWN_SetStringInWaveNote(output, SF_META_YAXISLABEL, lbnKey) + WAVE allLBNKeysUnique = GetUniqueEntries(allLBNKeys) - return output + return allLBNKeysUnique +End + +static Function SF_SetSweepXAxisTickLabels(WAVE output, WAVE/Z selectDataPlainOrArray) + + variable numSelected + + if(!WaveExists(selectDataPlainOrArray)) + return NaN + endif + + if(IsWaveRefWave(selectDataPlainOrArray)) + if(DimSize(selectDataPlainOrArray, ROWS) > 1) + return NaN + endif + + WAVE/WAVE selectDataArray = selectDataPlainOrArray + + WAVE/WAVE singleSelectData = selectDataArray[0] + WAVE/Z selectData = singleSelectData[%SELECTION] + + if(!WaveExists(selectData)) + return NaN + endif + else + WAVE selectData = selectDataPlainOrArray + endif + + numSelected = DimSize(selectData, ROWS) + + Make/FREE/N=(numSelected) xTickPositions = selectData[p][%SWEEP] + Make/T/FREE/N=(numSelected) xTickLabels = num2str(selectData[p][%SWEEP]) + + JWN_SetWaveInWaveNote(output, SF_META_XTICKPOSITIONS, xTickPositions) + JWN_SetWaveInWaveNote(output, SF_META_XTICKLABELS, xTickLabels) End static Function/WAVE SF_OperationLog(variable jsonId, string jsonPath, string graph) @@ -6989,3 +7476,63 @@ static Function/S SF_MatchSweepMapColumn(string graph, string match, string colL return uniqueEntries[0] End + +Function TraceValueDisplayHook(STRUCT WMTooltipHookStruct &s) + + string name, msg, allTraces, trace, tooltip, match, options, win, valueStr, tagText + variable numTraces, i + + // traceName is set only for graphs and only if the mouse hovered near a trace + if(IsEmpty(s.traceName)) + return 0 + endif + + win = s.winName + + tooltip = "" + allTraces = TraceNameList(win, ";", 1 + 2) + + numTraces = ItemsInList(allTraces) + for(i = 0; i < numTraces; i += 1) + trace = StringFromList(i, allTraces) + + sprintf options, "WINDOW:%s;ONLY:%s;DELTAX:24;DELTAY:24", win, trace + match = TraceFromPixel(s.mouseLoc.h, s.mouseLoc.v, options) + + if(IsEmpty(match)) + continue + endif + + WAVE wv = TraceNameToWaveRef(win, trace) + + name = JWN_GetStringFromWaveNote(wv, SF_META_LEGEND_LINE_PREFIX) + + if(IsEmpty(name)) + // not a labnotebook/analysis function parameter + continue + endif + + if(IsNumericWave(wv)) + tagText = JWN_GetStringFromWaveNote(wv, SF_META_TAG_TEXT) + if(IsEmpty(tagText)) + valueStr = num2str(wv[s.row][s.column][s.layer][s.chunk]) + else + valueStr = ReplaceString("\r", tagText, "\r" + ReplicateString(" ", strlen(name) + 2)) + endif + elseif(IsTextWave(wv)) + WAVE/T wvText = wv + valueStr = wvText[s.row][s.column][s.layer][s.chunk] + endif + + sprintf msg, "%s: %s\r", name, valueStr + tooltip += msg + endfor + + if(!IsEmpty(tooltip)) + s.tooltip = "
" + RemoveEnding(tooltip, "\r") + "
" + s.isHtml = 1 + return 1 + endif + + return 0 +End diff --git a/Packages/MIES/MIES_SweepFormula_Helpers.ipf b/Packages/MIES/MIES_SweepFormula_Helpers.ipf index 6d2e349681..1d7ce7efb6 100644 --- a/Packages/MIES/MIES_SweepFormula_Helpers.ipf +++ b/Packages/MIES/MIES_SweepFormula_Helpers.ipf @@ -501,7 +501,8 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD variable i, j, rangeStart, rangeEnd, sweepNo, isSingleRange variable chanNr, chanType, cIndex, isSweepBrowser, mapIndex variable numSelected, index, numRanges, sweepSize, samplingInterval, samplingOffset - variable rangeStartIndex, rangeEndIndex + variable rangeStartIndex, rangeEndIndex, colorGroup + string dimLabel, device, experiment ASSERT(WindowExists(graph), "graph window does not exist") @@ -615,6 +616,9 @@ static Function/WAVE SFH_GetSweepsForFormulaImpl(string graph, WAVE/WAVE selectD JWN_SetNumberInWaveNote(rangedSweepData, SF_META_SWEEPMAPINDEX, mapIndex) endif + colorGroup = GetUniqueInteger() + JWN_SetNumberInWaveNote(rangedSweepData, SF_META_COLOR_GROUP, colorGroup) + EnsureLargeEnoughWave(output, indexShouldExist = index) output[index] = rangedSweepData index += 1 @@ -1430,7 +1434,7 @@ Function/WAVE SFH_GetStimsetRange(string graph, WAVE data, WAVE selectData) // data prior to a2172f03 (Added generations of epoch information wave, 2019-05-22) // remove total onset delay and termination delay iff we have neither dDAQ nor oodDAQ enabled - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) ASSERT(WaveExists(numericalValues), "Missing numerical labnotebook") // 778969b0 (DC_PlaceDataInITCDataWave: Document all other settings from the DAQ groupbox, 2015-11-26) @@ -1496,7 +1500,8 @@ Function [WAVE adaptedRange, WAVE/T epochRangeNames] SFH_GetNumericRangeFromEpoc SFH_ASSERT(IsTextWave(epochPatterns) && !DimSize(epochPatterns, COLS), "Expected 1d text wave for epoch specification") if(BSP_IsSweepBrowser(graph)) - DFREF sweepDFR = SB_GetSweepDF(graph, mapIndex) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + DFREF sweepDFR = SB_GetSweepDataPathFromIndex(sweepBrowserDFR, mapIndex) else DFREF deviceDFR = DB_GetDeviceDF(graph) DFREF sweepDFR = GetSingleSweepFolder(deviceDFR, sweepNo) @@ -1800,11 +1805,13 @@ End /// @param mapIndex if graph is a SweepBrowser then a non-NaN mapIndex into sweepMap, otherwise must be NaN Function [WAVE numericalValues, WAVE textualValues, DFREF sweepDFR] SFH_GetLabNoteBooksAndDFForSweep(string graph, variable sweepNo, variable mapIndex) - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) + WAVE textualValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_TEXTUAL_VALUES) if(BSP_IsSweepBrowser(graph)) ASSERT(!IsNaN(mapIndex), "Can not work with NaN as mapIndex") - DFREF sweepDFR = SB_GetSweepDF(graph, mapIndex) + DFREF sweepBrowserDFR = SB_GetSweepBrowserFolder(graph) + DFREF sweepDFR = SB_GetSweepDataPathFromIndex(sweepBrowserDFR, mapIndex) return [numericalValues, textualValues, sweepDFR] endif @@ -1818,22 +1825,25 @@ End /// @brief Function returns the correct numerical and textual LNB for a given sweepNumber /// -/// @param graph name of graph window -/// @param sweepNo sweep number -/// @param mapIndex if graph is a SweepBrowser then a non-NaN mapIndex into sweepMap, otherwise must be NaN -Function [WAVE numericalValues, WAVE textualValues] SFH_GetLabNoteBooksForSweep(string graph, variable sweepNo, variable mapIndex) +/// @param graph name of graph window +/// @param sweepNo sweep number +/// @param logbookWaveType one of @ref LabnotebookWaveTypes +/// @param mapIndex if graph is a SweepBrowser then a non-NaN mapIndex into sweepMap, otherwise must be NaN +Function/WAVE SFH_GetLabNoteBookForSweep(string graph, variable sweepNo, variable mapIndex, variable logbookWaveType) + + string device, datafolder if(BSP_IsSweepBrowser(graph)) ASSERT(!IsNaN(mapIndex), "Can not work with NaN as mapIndex") + WAVE/T sweepMap = SB_GetSweepMap(graph) - [WAVE numericalValues, WAVE textualValues] = SB_GetLabNotebooks(sweepMap, mapIndex) - return [numericalValues, textualValues] + datafolder = sweepMap[mapIndex][%DataFolder] + device = sweepMap[mapIndex][%Device] + + return SB_GetLogbookWave(graph, LBT_LABNOTEBOOK, logbookWaveType, dataFolder = dataFolder, device = device) endif ASSERT(IsNaN(mapIndex), "Window is DataBrowser, but got a mapIndex into a sweepMap") - WAVE/Z numericalValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_NUMERICAL_VALUES, sweepNumber = sweepNo) - WAVE/Z textualValues = BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, LBN_TEXTUAL_VALUES, sweepNumber = sweepNo) - - return [numericalValues, textualValues] + return BSP_GetLogbookWave(graph, LBT_LABNOTEBOOK, logbookWaveType, sweepNumber = sweepNo) End diff --git a/Packages/MIES/MIES_SweepFormula_PSX.ipf b/Packages/MIES/MIES_SweepFormula_PSX.ipf index a2ef1a036a..8886eb95af 100644 --- a/Packages/MIES/MIES_SweepFormula_PSX.ipf +++ b/Packages/MIES/MIES_SweepFormula_PSX.ipf @@ -1109,7 +1109,8 @@ Function PSX_CollectResolvedRanges(string graph, WAVE range, WAVE singleSelectDa chanType = singleSelectData[0][%CHANNELTYPE] mapIndex = singleSelectData[0][%SWEEPMAPINDEX] - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) + WAVE textualValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_TEXTUAL_VALUES) SFH_ASSERT(WaveExists(textualValues) && WaveExists(numericalValues), "LBN not found for sweep " + num2istr(sweepNo)) [WAVE resolvedRanges, WAVE/T epochRangeNames] = SFH_GetNumericRangeFromEpoch(graph, numericalValues, textualValues, range, sweepNo, chanType, chanNr, mapIndex) @@ -1339,10 +1340,6 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r JWN_SetWaveInWaveNote(results, SF_META_XVALUES, xValues) - Make/FREE/T nonFiniteTickLabels = {num2str(-Inf), num2str(NaN), num2str(+Inf)} - JWN_SetWaveInWaveNote(results, SF_META_XTICKLABELS, nonFiniteTickLabels) - JWN_SetWaveInWaveNote(results, SF_META_XTICKPOSITIONS, {-1, 0, 1}) - break case "count": MatrixOP/FREE results = numRows(resultsRaw) @@ -1438,6 +1435,10 @@ static Function/WAVE PSX_OperationStatsImpl(string graph, string id, WAVE/WAVE r break case "nonfinite": JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "Non-finite values of " + LowerStr(propLabelAxis)) + + Make/FREE/T nonFiniteTickLabels = {num2str(-Inf), num2str(NaN), num2str(+Inf)} + JWN_SetWaveInWaveNote(output, SF_META_XTICKLABELS, nonFiniteTickLabels) + JWN_SetWaveInWaveNote(output, SF_META_XTICKPOSITIONS, {-1, 0, 1}) break case "count": JWN_SetStringInWaveNote(output, SF_META_XAXISLABEL, "NA") @@ -2972,7 +2973,8 @@ static Function/S PSX_GenerateComboKey(string graph, WAVE selectData, WAVE range chanType = selectData[0][%CHANNELTYPE] mapIndex = selectData[0][%SWEEPMAPINDEX] - [WAVE numericalValues, WAVE textualValues] = SFH_GetLabNoteBooksForSweep(graph, sweepNo, mapIndex) + WAVE numericalValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_NUMERICAL_VALUES) + WAVE textualValues = SFH_GetLabNoteBookForSweep(graph, sweepNo, mapIndex, LBN_TEXTUAL_VALUES) // Introduced in 7e903ed8 (GetSweepSettingsTextWave: Add device as entry, 2023-01-03) device = GetLastSettingTextIndep(textualValues, sweepNo, "Device", DATA_ACQUISITION_MODE) @@ -3565,7 +3567,7 @@ static Function PSX_CreatePSXGraphAndSubwindows(string win, string graph, STRUCT WAVE sweepDataFiltOff = GetPSXSweepDataFiltOffWaveFromDFR(comboDFR) WAVE sweepDataFiltOffDeconv = GetPSXSweepDataFiltOffDeconvWaveFromDFR(comboDFR) - [STRUCT RGBColor color] = SF_GetTraceColor(graph, plotMetaData.opStack, sweepData) + [STRUCT RGBColor color] = SF_GetTraceColor(graph, plotMetaData.opStack, sweepData, $"") AppendToGraph/W=$win/C=(color.red, color.green, color.blue)/L=leftFiltOff sweepDataFiltOff AppendToGraph/W=$win/L=leftFiltOff peakYAtFilt vs peakX diff --git a/Packages/MIES/MIES_Utilities_List.ipf b/Packages/MIES/MIES_Utilities_List.ipf index 55175ce86f..1f80094051 100644 --- a/Packages/MIES/MIES_Utilities_List.ipf +++ b/Packages/MIES/MIES_Utilities_List.ipf @@ -213,3 +213,14 @@ Function/S MergeLists(string l1, string l2, [string sep]) return l2 End + +/// @brief Replace the list separator (semicolon) by a CR +Function/S PrepareListForDisplay(string list) + + if(StringEndsWith(list, ";")) + list = ReplaceString(";", list, "\r") + list = RemoveEnding(list, "\r") + endif + + return list +End diff --git a/Packages/MIES/MIES_WaveBuilder.ipf b/Packages/MIES/MIES_WaveBuilder.ipf index 503fe58e20..f6437dbb95 100644 --- a/Packages/MIES/MIES_WaveBuilder.ipf +++ b/Packages/MIES/MIES_WaveBuilder.ipf @@ -2522,44 +2522,21 @@ Function WB_AddAnalysisParameterIntoWPT(WPT, name, [var, str, wv]) string str WAVE wv - string type, value, formattedString, params + string params ASSERT(ParamIsDefault(var) + ParamIsDefault(str) + ParamIsDefault(wv) == 2, "Expected one of var, str or wv") + params = WPT[%$"Analysis function params (encoded)"][%Set][INDEP_EPOCH_TYPE] + if(!ParamIsDefault(var)) - type = "variable" - // numbers never need URL encoding - value = num2str(var) + AFH_AddAnalysisParameterToParams(params, name, var = var) elseif(!ParamIsDefault(str)) - type = "string" - value = URLEncode(str) + AFH_AddAnalysisParameterToParams(params, name, str = str) elseif(!ParamIsDefault(wv)) - ASSERT(DimSize(wv, ROWS) > 0, "Expected non-empty wave") - if(IsTextWave(wv)) - type = "textwave" - Duplicate/T/FREE wv, wvText - wvText = UrlEncode(wvText) - value = TextWaveToList(wvText, "|") - else - type = "wave" - // numbers never need URL encoding - value = NumericWaveToList(wv, "|", format = "%.15g") - endif - endif - - ASSERT(AFH_IsValidAnalysisParameter(name), "Name is not a legal non-liberal igor object name") - ASSERT(!GrepString(value, "[=:,;]+"), "Broken URL encoding. Written entry contains invalid characters (one of `=:,;`)") - ASSERT(AFH_IsValidAnalysisParamType(type), "Invalid type") - - params = WPT[%$"Analysis function params (encoded)"][%Set][INDEP_EPOCH_TYPE] - -#ifndef AUTOMATED_TESTING - if(WhichListItem(name, AFH_GetListOfAnalysisParamNames(params)) != -1) - printf "Parameter \"%s\" is already present and will be overwritten!\r", name + AFH_AddAnalysisParameterToParams(params, name, wv = wv) endif -#endif - WPT[%$"Analysis function params (encoded)"][%Set][INDEP_EPOCH_TYPE] = ReplaceStringByKey(name, params, type + "=" + value, ":", ",", 0) + WPT[%$"Analysis function params (encoded)"][%Set][INDEP_EPOCH_TYPE] = params End /// @brief Internal use only diff --git a/Packages/MIES/MIES_WaveDataFolderGetters.ipf b/Packages/MIES/MIES_WaveDataFolderGetters.ipf index 4a995dce53..3f9e958f31 100644 --- a/Packages/MIES/MIES_WaveDataFolderGetters.ipf +++ b/Packages/MIES/MIES_WaveDataFolderGetters.ipf @@ -759,6 +759,15 @@ threadsafe Function/S GetSweepFormulaPathAsString() return GetMiesPathAsString() + ":SweepFormula" End +/// @brief Return a Nx3 wave usable for setting trace colors +Function/WAVE GetColorWave(variable numEntries) + + Make/FREE/N=(numEntries, 3)/R traceColors + SetDimensionLabels(traceColors, "Red;Green;Blue", COLS) + + return traceColors +End + /// @brief Returns a data folder reference to the call once folder threadsafe Function/DF GetCalledOncePath() return createDFWithAllParents(GetCalledOncePathAsString()) diff --git a/Packages/MIES/SweepFormulaHelp.ifn b/Packages/MIES/SweepFormulaHelp.ifn index d271660586..7c5f49404f 100644 Binary files a/Packages/MIES/SweepFormulaHelp.ifn and b/Packages/MIES/SweepFormulaHelp.ifn differ diff --git a/Packages/doc/SweepFormula.rst b/Packages/doc/SweepFormula.rst index 1af97f6b24..bcef4a7f25 100644 --- a/Packages/doc/SweepFormula.rst +++ b/Packages/doc/SweepFormula.rst @@ -1047,46 +1047,71 @@ labnotebook .. code-block:: bash - labnotebook(string key[, array selectData [, string entrySourceType]]) + labnotebook(array keys[, array selectData [, string entrySourceType]]) The labnotebook function returns the (case insensitive) `key` entry from the -labnotebook for the selected channel and sweep combination(s). For selectData either a single `select` or an array of `select`s can be specified. -If an array is specified then over each selection is iterated independently. +labnotebook for the selected channel and sweep combination(s). For selectData +either a single `select` or an array of select's can be specified. If an array +is specified then over each selection is iterated independently. The optional `entrySourceType` can be one of the constants `DataAcqModes` for data acquisition modes as defined in `../MIES/MIES_Constants.ipf`. If the -`entrySourceType` is omitted it defaults to `DATA_ACQUISITION_MODE`. +`entrySourceType` is omitted it defaults to `DATA_ACQUISITION_MODE`. Wildcard +expressions using `*`/`!` are also supported. See :ref:`here ` +for a list of stock labnotebook entries. -When the optional select argument is omitted, `select()` is used as default that includes all displayed sweeps and channels. +When the optional select argument is omitted, `select()` is used as default +that includes all displayed sweeps and channels. -The `labnotebook` operation returns a data wave for each selected sweep/channel combination. Each data wave contains a single element, that is depending on the -requested labnotebook entry numeric or textual. +The `labnotebook` operation returns a data wave for each selected sweep/channel +combination. Each data wave contains a single element, that is depending on the +requested labnotebook entry numerical or textual. -The returned data type is `SF_DATATYPE_LABNOTEBOOK`. -If input data type is `SF_DATATYPE_SWEEP` from the data operation the sweep meta data is transferred to the returned data waves. -The default suggested x-axis values for the formula plotter are sweep numbers. -The suggested y-axis label is the labnotebook key. +The returned data type is `SF_DATATYPE_LABNOTEBOOK`. If input data type is +`SF_DATATYPE_SWEEP` from the data operation the sweep meta data is transferred +to the returned data waves. The default suggested x-axis values for the formula +plotter are sweep numbers. .. code-block:: bash - max( - data( - select( - selrange( - cursors(A, B) - ), - selchannels(AD) - ) - ) - ) - vs labnotebook( "set cycle count", select(selchannels(AD)), DATA_ACQUISITION_MODE ) + labnotebook("*QC") + The function searches for numeric entries in the labnotebook first and then for -text entries. It returns a null wave if no match was found. +text entries. + +anaFuncParam +"""""""""""" + +.. code-block:: bash + + anafuncparam(array keys[, array selectData]) + +The `anafuncparam` function returns the values of the requested analysis function +parameters for the selected channel and sweep combination(s). For selectData +either a single `select` or an array of select's can be specified. If an array +is specified then over each selection is iterated independently. +Wildcard expressions using `*`/`!` are also supported. +See :ref:`here
` for a list of parameters +from analysis functions shipped with MIES. + +When the optional select argument is omitted, `select()` is used as default +that includes all displayed sweeps and channels. + +The returned data type is `SF_DATATYPE_ANAFUNCPARAM`. The default suggested +x-axis values for the formula plotter are sweep numbers. + +.. code-block:: bash + + anafuncparam("SlopePercentage", select()) + + anafuncparam(["OperationMode", "DA*"]) + + anafuncparam("*") findlevel """"""""" diff --git a/Packages/tests/Basic/Basic.pxp b/Packages/tests/Basic/Basic.pxp index 8d827a4be5..d84ac546f9 100644 Binary files a/Packages/tests/Basic/Basic.pxp and b/Packages/tests/Basic/Basic.pxp differ diff --git a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf index 7f14bb7c5e..b6e91f85b6 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_Operations.ipf @@ -2078,11 +2078,11 @@ static Function TestOperationPowerSpectrum() End static Function TestOperationLabNotebook() - variable i, j, sweepNumber, channelNumber, numSweeps, numChannels - string str, key + variable i, j, sweepNumber, channelNumber, numSweeps, numChannels, idx + string str, key, axLabel, browser, yAxisLabel string textKey = LABNOTEBOOK_USER_PREFIX + "TEXTKEY" - string textValue = "TestText" + string textValue = "TestText1;TestText2;" string win, device @@ -2104,12 +2104,79 @@ static Function TestOperationLabNotebook() WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) - str = "labnotebook(" + textKey + ")" + str = "labnotebook(" + textKey + ", select(selchannels(AD2), selsweeps([0, 1])))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + Make/FREE/D refDataTextAnchor = {0.0} + for(WAVE/D data : dataRef) + CHECK_EQUAL_WAVES(data, refDataTextAnchor, mode = WAVE_DATA) + CHECK_EQUAL_STR("TestText1\rTestText2", JWN_GetStringFromWaveNote(data, SF_META_TAG_TEXT)) + CHECK_EQUAL_STR(textKey, JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX)) + + sweepNumber = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) + CHECK_EQUAL_VAR(sweepNumber, idx) + + WAVE xValues = JWN_GetNumericWaveFromWaveNote(data, SF_META_XVALUES) + Make/FREE xValuesRef = {idx} + CHECK_EQUAL_WAVES(xValues, xValuesRef, mode = WAVE_DATA) + idx += 1 + endfor + + // no such key + str = "labnotebook(\"I_DONT_EXIST\")" WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) - Make/FREE/T textRefData = {textValue} - for(WAVE/T dataT : dataRef) - CHECK_EQUAL_WAVES(dataT, textRefData, mode = WAVE_DATA) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 40) + Make/FREE/D refDataTextAnchor = {NaN} + for(WAVE/D data : dataRef) + CHECK_EQUAL_WAVES(data, refDataTextAnchor, mode = WAVE_DATA) + endfor + + // multiple keys + str = "labnotebook([\"ADC\", \"Operating Mode\"], select(selchannels(AD2), selsweeps([0])), DATA_ACQUISITION_MODE)" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 2) + WAVE firstEntry = dataRef[0] + Make/D/FREE refContents = {2} + CHECK_EQUAL_WAVES(firstEntry, refContents, mode = WAVE_DATA) + + WAVE secondEntry = dataRef[1] + Make/D/FREE refContents = {1} + CHECK_EQUAL_WAVES(secondEntry, refContents, mode = WAVE_DATA) + + // no match with wildcards + str = "labnotebook([\"eee*\"], select(selchannels(AD2), selsweeps([0])), DATA_ACQUISITION_MODE)" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) + + // match with wildcard and special QC format + str = "labnotebook([\"*random QC\"], select(selchannels(AD0), selsweeps([0, 1, 2])), DATA_ACQUISITION_MODE)" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 3) + idx = 0 + for(WAVE/D data : dataRef) + Make/D/FREE refData = {idx == 1 ? 1 : 0} + CHECK_EQUAL_WAVES(data, refData, mode = WAVE_DATA) + CHECK_EQUAL_STR("", JWN_GetStringFromWaveNote(data, SF_META_TAG_TEXT)) + CHECK_EQUAL_STR("USER_random QC", JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX)) + + sweepNumber = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) + CHECK_EQUAL_VAR(sweepNumber, idx) + + WAVE xValues = JWN_GetNumericWaveFromWaveNote(data, SF_META_XVALUES) + Make/FREE xValuesRef = {idx} + CHECK_EQUAL_WAVES(xValues, xValuesRef, mode = WAVE_DATA) + idx += 1 endfor + + WAVE yTickLabels = JWN_GetTextWaveFromWaveNote(dataRef, SF_META_YTICKLABELS) + CHECK_EQUAL_TEXTWAVES(yTickLabels, {"Failed", "Passed"}) + + // key with unit + str = "labnotebook([\"*other KEY\"], select(selchannels(AD0), selsweeps([0])), DATA_ACQUISITION_MODE)" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 1) + + yAxisLabel = JWN_GetStringFromWaveNote(dataRef, SF_META_YAXISLABEL) + CHECK_EQUAL_STR(yAxisLabel, "Unit (Hz)") End static Function TestOperationLabnotebookHelper(string win, string formula, WAVE wRef) @@ -3130,3 +3197,136 @@ static Function DefaultFormulaWorks() CHECK_EQUAL_WAVES(chan1, chan1Ref, mode = WAVE_DATA) CHECK_EQUAL_WAVES(chan3, chan3Ref, mode = WAVE_DATA) End + +static Function TestOperationAnaFuncParam() + variable numSweeps, numChannels, idx, sweepNo + string win, device, params, str, textKey, textValue + + textKey = LABNOTEBOOK_USER_PREFIX + "TEXTKEY" + textValue = "TestText1;TestText2;" + + [win, device] = CreateEmptyUnlockedDataBrowserWindow() + + [numSweeps, numChannels, WAVE/U/I channels] = FillFakeDatabrowserWindow(win, device, XOP_CHANNEL_TYPE_ADC, textKey, textValue) + win = GetCurrentWindow() + + Make/FREE/T/N=(1, 1) funcParamsKey + funcParamsKey[0][0] = ANALYSIS_FUNCTION_PARAMS_LBN + + // sweep 0 + params = "" + AFH_AddAnalysisParameterToParams(params, "var", var = 123) + AFH_AddAnalysisParameterToParams(params, "str", str = "abcd") + AFH_AddAnalysisParameterToParams(params, "strList", str = "abcd;efgh;") + AFH_AddAnalysisParameterToParams(params, "wv", wv = {1, 2, 3}) + AFH_AddAnalysisParameterToParams(params, "wvText", wv = ListToTextWave("hijk;lmno;", ";")) + + WAVE/T funcParamsVal = LBN_GetTextWave() + funcParamsVal[7] = params + Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 funcParamsVal + ED_AddEntriesToLabnotebook(funcParamsVal, funcParamsKey, 0, device, DATA_ACQUISITION_MODE) + + // sweep 1 + params = "" + AFH_AddAnalysisParameterToParams(params, "var", var = 567) + + WAVE/T funcParamsVal = LBN_GetTextWave() + funcParamsVal[7] = params + Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 funcParamsVal + ED_AddEntriesToLabnotebook(funcParamsVal, funcParamsKey, 1, device, DATA_ACQUISITION_MODE) + + // no such key + str = "anaFuncParam(\"I_DONT_EXIST\")" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) + + // no params in sweep + str = "anaFuncParam(\"var\", select(selchannels(), selsweeps([2])))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) + + // no wildcard match + str = "anaFuncParam(\"SOME NAME*\")" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) + + // no select data + str = "anaFuncParam(\"var\", select(selchannels(), selsweeps([100])))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 0) + + // single match (var) + str = "anaFuncParam(\"var\", select(selchannels(AD0), selsweeps()))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 10) + for(WAVE/D data : dataRef) + if(idx == 0) + CHECK_EQUAL_WAVES(data, {123}, mode = WAVE_DATA) + elseif(idx == 1) + CHECK_EQUAL_WAVES(data, {567}, mode = WAVE_DATA) + else + CHECK_EQUAL_WAVES(data, {NaN}, mode = WAVE_DATA) + endif + + CHECK_EQUAL_STR("var", JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX)) + + WAVE xValues = JWN_GetNumericWaveFromWaveNote(data, SF_META_XVALUES) + Make/FREE xValuesRef = {idx} + CHECK_EQUAL_WAVES(xValues, xValuesRef, mode = WAVE_DATA) + + sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) + CHECK_EQUAL_VAR(sweepNo, idx) + + idx += 1 + endfor + + // wildcard match (str*) + str = "anaFuncParam(\"str*\", select(selchannels(AD0), selsweeps([0, 1])))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 4) + idx = 0 + Make/FREE dataPoints = {0, NaN, 0, NaN} + Make/FREE/T paramNames = {"str", "str", "strList", "strList"} + Make/FREE/T paramValues = {"abcd", "", "abcd\refgh", ""} + for(WAVE/D data : dataRef) + + CHECK_EQUAL_WAVES(data, {dataPoints[idx]}, mode = WAVE_DATA) + CHECK_EQUAL_STR(paramNames[idx], JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX)) + + WAVE xValues = JWN_GetNumericWaveFromWaveNote(data, SF_META_XVALUES) + Make/FREE xValuesRef = {mod(idx, 2)} + CHECK_EQUAL_WAVES(xValues, xValuesRef, mode = WAVE_DATA) + + sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) + CHECK_EQUAL_VAR(sweepNo, xValuesRef[0]) + + CHECK_EQUAL_STR(paramValues[idx], JWN_GetStringFromWaveNote(data, SF_META_TAG_TEXT)) + + idx += 1 + endfor + + // wildcard match (wv*) + str = "anaFuncParam(\"wv*\", select(selchannels(AD0), selsweeps([0, 1])))" + WAVE/WAVE dataRef = SF_ExecuteFormula(str, win, useVariables = 0) + CHECK_EQUAL_VAR(DimSize(dataRef, ROWS), 4) + idx = 0 + Make/FREE dataPoints = {0, NaN, 0, NaN} + Make/FREE/T paramNames = {"wv", "wv", "wvText", "wvText"} + Make/FREE/T paramValues = {"1\r2\r3", "", "hijk\rlmno", ""} + for(WAVE/D data : dataRef) + + CHECK_EQUAL_WAVES(data, {dataPoints[idx]}, mode = WAVE_DATA) + CHECK_EQUAL_STR(paramNames[idx], JWN_GetStringFromWaveNote(data, SF_META_LEGEND_LINE_PREFIX)) + + WAVE xValues = JWN_GetNumericWaveFromWaveNote(data, SF_META_XVALUES) + Make/FREE xValuesRef = {mod(idx, 2)} + CHECK_EQUAL_WAVES(xValues, xValuesRef, mode = WAVE_DATA) + + sweepNo = JWN_GetNumberFromWaveNote(data, SF_META_SWEEPNO) + CHECK_EQUAL_VAR(sweepNo, xValuesRef[0]) + + CHECK_EQUAL_STR(paramValues[idx], JWN_GetStringFromWaveNote(data, SF_META_TAG_TEXT)) + + idx += 1 + endfor +End diff --git a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf index 9ccf61a608..b4e0c4e58d 100644 --- a/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf +++ b/Packages/tests/Basic/UTF_SweepFormula_PSX.ipf @@ -743,8 +743,7 @@ static Function StatsWorksWithResults([STRUCT IUTF_mData &m]) CHECK_WAVE(marker, NULL_WAVE) endif - WAVE/Z xTickPositionsRead = JWN_GetNumericWaveFromWaveNote(resultsRead, SF_META_XTICKPOSITIONS) - WAVE/Z xTickLabelsRead = JWN_GetTextWaveFromWaveNote(resultsRead, SF_META_XTICKLABELS) + WAVE/Z xTickPositionsRead = JWN_GetNumericWaveFromWaveNote(output, SF_META_XTICKPOSITIONS) if(WaveExists(xTickPositions)) CHECK_EQUAL_WAVES(xTickPositionsRead, xTickPositions, mode = WAVE_DATA) @@ -753,6 +752,8 @@ static Function StatsWorksWithResults([STRUCT IUTF_mData &m]) CHECK_WAVE(xTickPositions, NULL_WAVE) endif + WAVE/Z xTickLabelsRead = JWN_GetTextWaveFromWaveNote(output, SF_META_XTICKLABELS) + if(WaveExists(xTickLabels)) CHECK_EQUAL_WAVES(xTickLabelsRead, xTickLabels, mode = WAVE_DATA) else diff --git a/Packages/tests/Basic/UTF_Utils_List.ipf b/Packages/tests/Basic/UTF_Utils_List.ipf index 88760462ad..d54d87f1eb 100644 --- a/Packages/tests/Basic/UTF_Utils_List.ipf +++ b/Packages/tests/Basic/UTF_Utils_List.ipf @@ -105,3 +105,11 @@ Function RPFLI_Works() End /// @} + +Function PrepareListForDisplay_Works() + + CHECK_EQUAL_STR("", PrepareListForDisplay("")) + CHECK_EQUAL_STR("a", PrepareListForDisplay("a")) + CHECK_EQUAL_STR("a;b", PrepareListForDisplay("a;b")) + CHECK_EQUAL_STR("a\rb", PrepareListForDisplay("a;b;")) +End diff --git a/Packages/tests/UTF_HelperFunctions.ipf b/Packages/tests/UTF_HelperFunctions.ipf index 7ca497c6ed..05d1adc662 100644 --- a/Packages/tests/UTF_HelperFunctions.ipf +++ b/Packages/tests/UTF_HelperFunctions.ipf @@ -1480,6 +1480,16 @@ Function [variable numSweeps, variable numChannels, WAVE/U/I channels] FillFakeD Make/FREE/T/N=(1, 1) clampModeKeys = "Operating Mode" Make/FREE/T/N=(1, 1) textKeys = lbnTextKey + Make/FREE/T/N=(3, 1) qcKeys + qcKeys[0][0] = LABNOTEBOOK_USER_PREFIX + "random QC" + qcKeys[1][0] = LABNOTEBOOK_BINARY_UNIT + qcKeys[2][0] = LABNOTEBOOK_NO_TOLERANCE + + Make/FREE/T/N=(3, 1) otherKey + otherKey[0][0] = LABNOTEBOOK_USER_PREFIX + "other key" + otherKey[1][0] = "Hz" + otherKey[2][0] = LABNOTEBOOK_NO_TOLERANCE + DFREF dfr = GetDeviceDataPath(device) GetDAQDeviceID(device) @@ -1512,6 +1522,18 @@ Function [variable numSweeps, variable numChannels, WAVE/U/I channels] FillFakeD ED_AddEntryToLabnotebook(device, keys[0], values, overrideSweepNo = sweepNumber) ED_AddEntriesToLabnotebook(valuesText, textKeys, sweepNumber, device, mode) + WAVE qcValues = LBN_GetNumericWave() + // 0th channel maps to HS7 + qcValues[connections[0]] = mod(sweepNumber, 2) + Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 qcValues + ED_AddEntriesToLabnotebook(qcValues, qcKeys, sweepNumber, device, mode) + + WAVE otherValue = LBN_GetNumericWave() + // 1st channel maps to HS5 + otherValue[connections[1]] = i^2 + Redimension/N=(1, 1, LABNOTEBOOK_LAYER_COUNT)/E=1 otherValue + ED_AddEntriesToLabnotebook(otherValue, otherKey, sweepNumber, device, mode) + PGC_SetAndActivateControl(BSP_GetPanel(win), "popup_DB_lockedDevices", str = device) win = GetCurrentWindow()