.. highlightlang:: us .. _formula-interpreter-3---creating-script-formulas: Formula Interpreter 3 - Creating Script Formulas ================================================ Most formulas can be defined in the formula table or using the formula dialog in the data browser. Script formulas can be created for formulas which cannot defined in the table because they are too long or too complex. Script formulas can use all UniScript elements, such as if-else conditions and for-loops. .. _example-formula: Example Formula --------------- The following script formula computes power from speed and torque. :: // Power P in kW // T*2*PI*rpm // Speed rpm in 1/min // Trq T in Nm def _fi_P(bInfo) { ssChannels = "rpm, Trq"; if (bInfo) { return ["P"; .. "kW"; .. "Power"; .. ssChannels; .. ""; .. "%.2lf"]; } rpm = ch("rpm"); Trq = ch("Trq"); P = (rpm .* Trq) ./ 9549.3; set_ch("P", P, ssChannels); } In script formulas, the operators ``.*``, ``./``, ``.^`` should be used. The period before the operator means that the operation of vectors is performed on an element by element basis. ``rpm`` and ``Trq`` are vectors containing all data points. The result vector ``P`` contains as many elements as ``rpm`` and ``Trq``. If ``rpm`` and ``Trq`` contain a different number of data points, the function would be terminated with an error message. For MDF-, Famos- or ASAM-ODS-Data the formula can contain channels with different number of data points. In this case the formula interpreter will interpolate all used channels to the time base with the highest resolution. Example: In the following example the channels a is multiplied with b. a is measured with 10 Hz and b with 100 Hz. The formula result c will have a resolution of 100 Hz. :: def _fi_c(bInfo) { ssChannels = "a, b"; if (bInfo) { return ["c"; .. ""; .. ""; .. ssChannels]; } _fich_a = ch("a", "c"); // a 10 Hz -> interpolate to 10Hz _fich_b = ch("b", "c"); // b 100 Hz -> interpolate to 10Hz _fich_c = _fich_a .* _fich_b; set_ch("c", _fich_c, ssChannels); } .. _elements-of-script-formulas: Elements of Script Formulas --------------------------- In order for the formulas to be evaluated by UniPlot, the function must fulfill the following regulations: * A UniScript function specified for each formula. * Each formula produces one channel. * The name of a function begins with ``_fi_``. The function has one parameter ``bInfo``. If the function is called with ``bInfo == TRUE``, the function must return a string vector with at least 4 elements. The meaning of the elements is described in :ref:`query-function-information`. * The function can access the data of a NC file with the function :ref:`ch` Calculated data is written to the NC file using the :ref:`set_ch` function. * All UniScript functions can be called within the function. .. _query-function-information: Query Function Information -------------------------- If a formula function is called with the value ``bInfo == TRUE``, the function must return a string vector with at least 4 elements (see example :ref:`example-formula`): Element 1 The first item is the name of the formula. The name must adhere to the rules for channel names (see :ref:`nc_vardef`). Element 2 Short description of the formula. Element 3 Physical unit. Element 4 A list of the channel names that are accessed in the formula separated by a comma. Before the function is called, the formula interpreter checks whether the formula can be calculated. In addition, the interpreter checks whether the dependent channels were already calculated or whether the measuring channels are available in the file. Element 5 Name of a condition, e.g. Diesel. If a condition is specified, a function must be available, which returns the value TRUE (1) or FALSE (0). If the formula can be calculated, the function returns the value TRUE (1), otherwise the value FALSE (0). The function begins with the prefix ``_fiis_`` followed by the name of the condition in lowercase letters, e.g. ``_fiis_diesel``. Element 6 Number format definition, e.g. ``%g`` or ``%.2lf``. You'll find a detailed description of the format definition in the documentation for the :ref:`printf` function. .. _definition-of-a-condition: Definition of a Condition ------------------------- The following program listing shows the UniScript function for the condition "Diesel":: def _fiis_diesel() { ncid = get_ncid(); if (ncid == -1) { error(); return FALSE; } if (nc_attinq_datatype(ncid, NC_GLOBAL, "PEtrol___DIesel") == NC_CHAR) { ssEngineType = nc_attget(ncid, NC_GLOBAL, "PEtrol___DIesel"); if (ssEngineType == "DI") { return TRUE; } } return FALSE; } The function :ref:`get_ncid` returns the handle of the active NC file. Use the handle to retrieve information from the file. In the example, we checked whether the file contains the global attribute "PEtrol___DIesel ". If the attribute value is "DI", the function returns TRUE (1). In all other cases the return value is FALSE (0). This means that all formulas containing the value "Diesel" in the info text will only be applied on NC files where the attribute "PEtrol___DIesel" is "DI". Condition functions begin with the character sequence ``_fiis _`` followed by the function name, which must correspond to the name rules for UniScript functions. Only lowercase letters should be used for the name, e.g. ``_fiis_diesel``. Condition functions can be stored in an ic file in the directory :file:`UniPlot\\samples`. .. _user-defined-functions: User Defined Functions ---------------------- If the program which executes the conversion of table formulas into UniScript functions finds any function calls, e.g. ``sqrt()``, it replaces the name with ``_fif_sqrt()``. Instead of :ref:`sqrt`, the function ``_fif_sqrt()`` is called. The reason for this is that the UniScript function :ref:`sqrt` ignores "Missing Values" (see :ref:`channels-with-missing-values`) and will calculate complex numbers for negative values. The function ``_fif_sqrt()`` is defined as following:: def _fif_sqrt(rvData) { r = rvData; idx = find(rvData < 0 || rvData == MISSING_VALUE); if (idx[1] != 0) { r[idx] = MISSING_VALUE; } r = sqrt(r); if (idx[1] != 0) { r[idx] = MISSING_VALUE; } return r; } Accordingly, you can write your own functions for the formula table. .. _OnFormulaStartEval: .. _OnFormulaFinishEval: .. _the-function-onformulastarteval: OnFormulaStartEval ------------------ The function :ref:`OnFormulaStartEval` is called if the user starts the formula evaluation, e.g. over the :kbd:`F9` key. The function has one parameter: The netCDF file handle (NC handle). This function can check whether the formulas are to be applied to the active NC file or not. If the function does not exist, the formulas are calculated with all NC-files. The function has read access to the NC-file. In the following program listing the function checks whether the NC file contains a global attribute "Creator" that contains the string "Pasy". :: def OnFormulaStartEval(ncid) { if (nc_attinq_datatype(ncid, NC_GLOBAL, "Creator") == NC_CHAR) { ssCreator = nc_attget(ncid, NC_GLOBAL, "Creator"); if (strmatch("*Pasy*", ssCreator)) { return TRUE; } } return FALSE; } ``OnFormulaStartEval(ncid)`` functions can also be stored in an IC file in the directory :file:`UniPlot\\samples`. The ``OnFormulaStartEval`` can be used to set a different formula directory. If a function with the name :ref:`OnFormulaFinishEval` is defined, it will be invoked by UniPlot to finish the calculation. :ref:`OnFormulaFinishEval` can be used to set the original formula directory. :: def OnFormulaStartEval(ncid) { svOldDir = FE_SetFormulaDirectory("C:/formula"); WriteProfileString("Formula", "olddir", strcat(svOldDir, ";")); return TRUE; } def OnFormulaFinishEval() { ssOldDir = GetProfileString("Formula", "olddir"); FE_SetFormulaDirectory(ssOldDir); } Register Formula ---------------- If a new formla file has been added to the formula directory, the library ``formula.icl9`` must be updated. When the dialog :ref:`tools-formula-configuration` is closed with OK the library is updated. To manually update the library invoke ``_FE_UpdateFormulaList()``. :sub:`id-71732`