- Note
- This file: ./src/CommandLineInterface/doc/FunctionParameterStructure.md
The function parameter structure (FPS) exposes a function's internal variables for read and/or write. It is stored in shared memory, in /MILK_SHM_DIR/fpsname.fps.shm.
Steps to run FPS-enabled processes:
$ vim fpslist.txt # Edit file, listing functions and corresponding FPS names that will be used
$ milk-fpsinit -e cacao -C # create all (-C) FPS shared memory structure(s) and tmux sessions. Use cacao (-e) to launch commands
$ milk-fpsCTRL # FPS control tool, scan ALL FPSs (-m option force match with fpscmd/fpslist.txt)
1. Overview and background
1.1. Main elements
FPS-enabled functions have the following elements:
- The shared memory FPS: /tmp/fpsname.fps.shm
- A configuration process that manages the FPS entries
- A run process (the function itself)
1.2. FPS name
<fpsname> consists of a root name (string), and a series of optional integers. Note that the number of digits matters and is part of the name:
<fpsname> = <fpsnameroot>.<opt0>.<opt1>...
Examples:
myfps # simple name, no optional integers
myfps-000000 # optional integer 000000
myfps-000000-white-000002 # 3 optional args
- Warning
- The FPS name does not need to match the process or function name. FPS name is specified in the CLI function as described in
- Writing the CLI function (in <module>.c file)
.
1.3. FPS-related entities
name | Type | Description | Origin |
/MILK_SHM_DIR/fpsname.fps.shm | shared memory | FP structure | Created by FPS init function |
fpsname:run | tmux session window 2 | FPS control terminal | Set up by milk-fpsinit |
fpsname:conf | tmux session window 3 | where CONF runs | Set up by milk-fpsinit |
fpsname:run | tmux session | where RUN runs | Set up by milk-fpsinit |
/MILK_SHM_DIR/fpslog.tstamp.pid.FPSTYPE-fpsname | ASCII file | log files | Created by FPS processes |
/MILK_SHM_DIR/fpslog.FPSTYPE-fpsname | sym link | log file | points to latest FPS log file |
./fpsconf/fpsname/... | ASCII file | parameter value | OPTIONAL |
2. FPS user interface
Main steps to enable FPS-enabled function for fpsCTRL:
- Define which FPSs to enable: Add entry in ./fpslist.txt
- Set up FPSs entities: Run milk-fpsinit
- Control and monitor FPSs: start milk-fpsCTRL
These steps should ideally performed by a setup script.
2.1. Define which FPSs to enable with a <tt>fpslist.txt</tt> file
The user-provided fpslist.txt
file lists the functions and corresponding FPS names that will be in use:
# List of FPS-enabled function
# Column 1: root name used to name FPS
# Column 2: CLI command
# Column(s) 3: optional arguments, string
fpsrootname0 CLIcommand0
fpsrootname1 CLIcommand1 optarg0 optarg1
2.2. Set up FPSs entities
FPS are built by
$ milk-fpsinit
2.3. milk-fpsCTRL tool
The FPS control tool is started from the command line :
$ milk-fpsCTRL
A fifo is set up by milk-fpsCTRL to receive commands to start/stop the conf and run processes. Commands can also be issued directly from the milk-fpsCTRL GUI.
2.3.1. Starting a conf process
To start a run process, the user issues a command to the fifo :
echo "confstart fpsname-01" >> ${MILK_SHM_DIR}/${LOOPNAME}_fpsCTRL.fifo
The steps triggered by this command are :
- Command is processed by fifo command interpreter functionparameter_FPSprocess_cmdline()
- The fifo command interpreter resolves the fps entry, and runs functionparameter_CONFstart(fps, fpsindex)
- Pre-configured function fpsconfstart is executed within the tmux session :run window :
- fps CLI command is launched with argument "_CONFSTART_" :
- function_parameter_getFPSname_from_CLIfunc() called, sets data.FPS_CMDCODE = FPSCMDCODE_CONFSTART
2.2.2. Stopping a conf process
To stop a conf process, the user issues a command to the fifo :
echo "confstop fpsname-01" >> ${MILK_SHM_DIR}/${LOOPNAME}_fpsCTRL.fifo
The steps triggered by this command are :
- Command is processed by fifo command interpreter functionparameter_FPSprocess_cmdline()
- The fifo command interpreter resolves the fps entry, and runs functionparameter_CONFstop(fps, fpsindex)
- FUNCTION_PARAMETER_STRUCT_SIGNAL_CONFRUN flag is set to 0
2.2.3. Starting a run process
To start a run process, the user issues a command to the fifo :
echo "confstart fpsname-01" >> ${MILK_SHM_DIR}/${LOOPNAME}_fpsCTRL.fifo
The steps triggered by this command are :
- Command is processed by fifo command interpreter functionparameter_FPSprocess_cmdline()
- The fifo command interpreter resolves the fps entry, and runs functionparameter_RUNstart(fps, fpsindex)
- Pre-configured function fpsrunstart is executed within the tmux session :run window :
- fps CLI command is launched with argument "_RUNSTART_" :
- function_parameter_getFPSname_from_CLIfunc() called, sets data.FPS_CMDCODE = FPSCMDCODE_RUNSTART
2.3.4. Stopping a run process
To stop a run process, the user issues a command to the fifo :
echo "runstop fpsname-01" >> ${MILK_SHM_DIR}/${LOOPNAME}_fpsCTRL.fifo
The steps triggered by this command are :
- Command is processed by fifo command interpreter functionparameter_FPSprocess_cmdline()
- The fifo command interpreter resolves the fps entry, and runs functionparameter_RUNstop(fps, fpsindex)
- Pre-configured function fpsrunstop is executed within the tmux session :ctrl window :
- fps CLI command is launched with argument "_RUNSTOP_" :
- function_parameter_getFPSname_from_CLIfunc() called, sets data.FPS_CMDCODE = FPSCMDCODE_RUNSTOP
- function_parameter_execFPScmd() called, calls conf process (function data.FPS_CONFfunc)
- function_parameter_FPCONFsetup, called within conf process, configures variables for run stop
- CTRL-c sent to run tmux session to ensure exit
3. Writing the CLI function (in <module>.c file)
A single CLI function, named <functionname>_cli, will take the following arguments:
- arg1: A command code
- arg2+: Optional arguments
The command code is a string, and will determine the action to be executed:
_FPSINIT_
: Initialize FPS for the function, look for parameter values on filesystem
_CONFSTART_
: Start the FPS configuration process
_CONFSTOP_
: Stop the FPS configuration process
_RUNSTART_
: Start the run process
_RUNSTOP_
: Stop the run process
- Note
- Why Optional arguments to CLI function ?
-
Multiple instances of a C function may need to be running, each with its own FPS. Optional arguments provides a mechanism to differentiate the FPSs. They are appended to the FPS name following a dash. Optional arguments can be a number (usually integer) or a string.
Example source code below.
static errno_t ExampleFunction__cli()
{
if(data.FPS_CMDCODE != 0) {
data.FPS_CONFfunc = ExampleFunction_FPCONF;
data.FPS_RUNfunc = ExampleFunction_RUN;
return RETURN_SUCCESS;
}
if(
CLI_checkarg(1, CLIARG_FLOAT) +
CLI_checkarg(2, CLIARG_LONG)
== 0) {
ExampleFunction(
data.cmdargtoken[1].val.numf,
data.cmdargtoken[2].val.numl
);
return RETURN_SUCCESS;
} else {
return CLICMD_INVALID_ARG;
}
}
4. Writing function prototypes (in <module>.h)
errno_t ExampleFunction_FPCONF();
errno_t ExampleFunction_RUN();
errno_t ExampleFunction(long arg0num, long arg1num, long arg2num, long arg3num);
5. Writing CONF function (in source .c file)
Check function_parameters.h for full list of flags.
errno_t ExampleFunction_FPCONF()
{
fps_add_processinfo_entries(&fps);
uint64_t FPFLAG;
FPTYPE_INT64, FPFLAG_DEFAULT_INPUT, NULL;
int64_t param02default[4] = { 5, 0, 10, 5 };
FPFLAG = FPFLAG_DEFAULT_INPUT | FPFLAG_MINLIMIT | FPFLAG_MAXLIMIT;
FPFLAG &= ~FPFLAG_WRITECONF;
FPFLAG &= ~FPFLAG_WRITERUN;
FPTYPE_INT64, FPFLAG, ¶m02default);
float gaindefault[4] = { 0.01, 0.0, 1.0, 0.01 };
FPFLAG = FPFLAG_DEFAULT_INPUT | FPFLAG_MINLIMIT | FPFLAG_MAXLIMIT;
FPTYPE_FLOAT32, FPFLAG, &gaindefault);
FPTYPE_ONOFF, FPFLAG_DEFAULT_INPUT, NULL);
FPFLAG = FPFLAG_DEFAULT_INPUT_STREAM;
FPTYPE_STREAMNAME, FPFLAG, NULL);
FPTYPE_FILENAME, FPFLAG_DEFAULT_OUTPUT, NULL);
long timeavemode_default[4] = { 0, 0, 3, 0 };
option_timeavemode,
".option.timeavemode",
"Enable time window averaging (>0)",
&timeavemode_default);
double avedt_default[4] = { 0.001, 0.0001, 1.0, 0.001};
option_avedt,
".option.avedt",
"Averaging time window width",
&avedt_default);
if ( fps.parray[fpi_gainset].fpflag & FPFLAG_ONOFF )
{
fps.parray[fpi_gain].fpflag |= FPFLAG_WRITERUN;
fps.parray[fpi_gain].fpflag |= FPFLAG_USED;
fps.parray[fpi_gain].fpflag |= FPFLAG_VISIBLE;
}
else
{
fps.parray[fpi_gain].fpflag &= ~FPFLAG_WRITERUN;
fps.parray[fpi_gain].fpflag &= ~FPFLAG_USED;
fps.parray[fpi_gain].fpflag &= ~FPFLAG_VISIBLE;
}
return RETURN_SUCCESS;
}
6. Writing RUN function (in source .c file)
The RUN function will connect to the FPS and execute the run loop.
6.1. A simple _RUN example
errno_t ExampleFunction_RUN()
{
int param01 = functionparameter_GetParamValue_INT64(&fps, ".param01");
int param02 = functionparameter_GetParamValue_INT64(&fps, ".param02");
int gainwrite = functionparameter_GetParamValue_ONOFF(&fps, ".option.gainwrite");
float *gain = functionparameter_GetParamPtr_FLOAT32(&fps, ".status.loopcnt");
char imsname[FUNCTION_PARAMETER_STRMAXLEN];
strncpy(imsname, functionparameter_GetParamPtr_STRING(&fps, ".option.imname"), FUNCTION_PARAMETER_STRMAXLEN);
long IDim = read_sharedmem_image(imsname);
int loopOK = 1;
while( loopOK == 1 )
{
}
function_parameter_RUNexit( &fps );
return RETURN_SUCCESS;
}
6.2. Non-FPS fallback function
errno_t ExampleFunction(
long arg0num,
long arg1num,
long arg2num,
long arg3num
) {
long pindex = (long) getpid();
sprintf(data.FPS_name, "exfunc-%06ld", pindex);
data.FPS_CMDCODE = FPSCMDCODE_FPSINIT;
ExampleFunction_FPCONF();
functionparameter_SetParamValue_INT64(&fps, ".arg0", arg0);
functionparameter_SetParamValue_INT64(&fps, ".arg1", arg1);
functionparameter_SetParamValue_INT64(&fps, ".arg2", arg2);
functionparameter_SetParamValue_INT64(&fps, ".arg3", arg3);
function_parameter_struct_disconnect(&fps);
ExampleFunction_RUN();
return RETURN_SUCCESS;
}
6.3. RUN function with FPS and processinfo
In this example, the loop process supports both FPS and processinfo. This is the preferred way to code a loop process.
6.3.1. Simple example making extensive use of macros (concept - to be implemented)
errno_t MyFunction_RUN()
{
FPSPROCINFOLOOP_INIT("computes something", "add image1 to image2");
int param01 = functionparameter_GetParamValue_INT64(&fps, ".param01");
PROCESSINFO_SET_SEMSTREAMWAIT("inputstreamname");
imageID IDout = image_ID("output");
PROCINFOLOOP_START;
data.image[IDout].md[0].
write = 1;
PROCINFOLOOP_END;
}
6.3.2. Verbose example with customization
The example also shows using FPS to set the process realtime priority.
errno_t MyFunction_RUN()
{
char timestring[100];
mkUTtimestring_millisec(timestring);
functionparameter_SetParamValue_STRING(
&fps,
".out.timestring",
timestring);
int param01 = functionparameter_GetParamValue_INT64(&fps, ".param01");
int param02 = functionparameter_GetParamValue_INT64(&fps, ".param02");
char nameim[FUNCTION_PARAMETER_STRMAXLEN+1];
strncpy(nameim, functionparameter_GetParamPtr_STRING(&fps, ".option.nameim"), FUNCTION_PARAMETER_STRMAXLEN);
float *gain = functionparameter_GetParamPtr_FLOAT32(&fps, ".ctrl.gain");
processinfo = processinfo_setup(
data.FPS_name,
"computes something",
"add image1 to image2",
__FUNCTION__, __FILE__, __LINE__
);
processinfo->MeasureTiming = 1;
processinfo->RT_priority = 20;
processinfo->loopcntMax = 1000;
fps_to_processinfo(&fps, processinfo);
int loopOK = 1;
int loopOK = 1;
if(.... error condition ....)
{
processinfo_error(processinfo, "ERROR: no WFS reference");
return RETURN_FAILURE;
}
IDin = image_ID("inputstream");
PROCESSINFO_TRIGGERMODE_SEMAPHORE, -1);
imageID IDout0 = image_ID("output0");
imageID IDout1 = image_ID("output1");
processinfo_loopstart(processinfo);
while(loopOK==1)
{
loopOK = processinfo_loopstep(processinfo);
processinfo_exec_start(processinfo);
if(processinfo_compute_status(processinfo)==1)
{
data.image[IDout0].md[0].
write = 1;
data.image[IDout1].md[0].
write = 1;
}
processinfo_exec_end(processinfo);
processinfo_WriteMessage(processinfo, "loop running fine");
}
processinfo_cleanExit(processinfo);
fprintf(fpout1, "0.123445");
fclose(fpout1);
function_parameter_RUNexit( &fps );
return RETURN_SUCCESS;
}