SourceForge Logo


The module interface was created for data logger modules in the first line. Later it became obvious that it can also be usefull for mathematical modules.

Using modules:

To load a module on program start,provide a line like the following your visual.cfg file,:

logger: myinst.dri /visual/data/

Meaning of this line:
logger:tells the loader that a shared object is to be loaded and that it will use this interface.
log_bin.somodule name
myinst.drifilename of instruction listin your project directory
/visual/data/module dependent datahere: data storage directory

Loading process details:

The loader loads the module's code from a shared object file using dlopen(). Then it gets a pointer to the modules's setup() procedure using dlsym(handle, "setup__FPv"). It invokes the setup procedure with a pointer to the filename of the instruction list and the module specific data. The setup procedure registers the module with the spreadsheet. Then it marks the cells it is interested in.
The spreadsheet recalculation thread will invoke the registered callback function, whenever a marked cell's value has changed in the recent calculation.

Details of registration:

Each cell has a 32 bit field named cflags. Each bit corresponds to one module. The number of contemporaryly loaded modules is thus limited to 32. The spreadsheet maintains a list of the modules in module_registrations array. When a module wants to register, it invokes registerModule() with it's name and a pointer to a Client object. The Client object must contain the adress of the module's callback function.
registerModule() looks for the first free entry in the registrations table and stores name and callback adress there. The corresponding bit mask (0x00000001 for the first and 0x10000000 for the last module) is returned to the modules setup procedure. If no more modules can be registered, 0 would be returned. The module should "give up" then and exit setup returning an error code.
Now the module's setup() marks all interesting cells setting it's corresponding bit in the cell's cflags field (OR cflags with it's bit mask).

Callback on change:

The calculation thread goes through the list of all numeric values and sets the flags field of all those having value<> prevVal to ValChangedAll, thus indicating: This cell has changed since last invokation/action of flag owner. One bit in flags is reserved for modules.
Now,the calculation thread goes through the list of all numeric values and checks, whether cflags is not NULL (i.e. there is a module interested in). If so, it calls the respective module callback functions with a clientInfo struct conteining the sheet number and a pointer to the cell.

Hints on programming modules:

The module's callback function must be GUARANTEED to return in a short and predictable time to maintain real time capabilities. Currently, there is no means to abort such a function based on time/CPU usage. The callback function may NOT cause fatal errors, because they would terminate the recalculation thread.

Mathematical modules:

The module API can also be used to create modules for mathematical functions. Immagine a statistics module. It may mark cell V1, where some measured value is stored with each new measurement. The module would be invoked each time and add that value to an array it maintains internally. Then it could calculate average, standard deviation etc. and store the results back into the spreadsheet.

I have done one mathematical module for a very special purpose:
The diameter of some object is measured in four axises which intersect under 45 degrees angles. To these points a function is fitted representing diameter vs. angle using spline interpolation. The resulting contour function is numerically integrated to get the object's cross section area. This calculation is too complicated to do it in the spreadsheet itself. Cross section result is then written back to the spreadsheet, so it can be shown on HMI screen, logged etc..