Chapter 2. Kernel API

Table of Contents
A basic module
Fitness function, chromosomes, event restriction
Matrices, domains
Resource conflicts, slist, resource restriction
Timetable extensions
Dependent events
Miscellaneous

A basic module

Let's begin with a simple "hello world" type of a module:


#include "module.h"

int module_init(moduleoption *opt)
{
        debug("Hello world!");
        debug("Weight: %d", option_int(opt, "weight"));

        return(0);
}

As you can see this module contains only the module_init() function. This function is called right after the kernel loads the module and is the only symbol that must be defined in all fitness modules for Tablix kernels in 0.2.x branch (prototype is defined in modsup.h, see init_f). No other function in the module is called unless you specifically request so in module_init().

Note: module_init() is equivalent to init_mod() in 0.1.x kernels.

Parameter opt of module_init() is a structure holding all module options that were specified in the configuration file as <option> tags. You can get values of these options from the moduleoption structure by using following two functions:

char *option_str(moduleoption *opt, char *name)

This function returns the string contents of the option with the name name or returns NULL if the option was not specified in the XML file. You should free the returned string after use with free().

int option_int(moduleoption *opt, char *name)

This function is equal to option_str(). Additionally it converts the contents of the option to an integer. If that is not possible or the option was not found, it returns INT_MIN.

Two options are always defined: The weight option always holds the integer value of the weight for this module that was specified in the XML configuration file. The mandatory option always contains either integer 1 or integer 0, depending whether the mandatory option for this module was set to "yes" or "no" in the configuration file.

Warning

Pointer to the linked list of module options is only passed to the module_init() function. Immediately after this function returns the memory used by the list is freed. This means that this pointer is only valid in module_init().

This simple module also demonstrates the use of the error reporting functions that are defined in error.h. Functions error(), info() and debug() can be used to report various warnings and errors back to the user. They are used much like the printf function from the standard library.

You can also see that module_init() returns 0 in this case. This means that there were no errors. In case of errors this function should return a value less than zero. The same holds for all other functions we will later add to our fitness module. In fact that is the case with most functions in Tablix that return an integer.

You should avoid using fatal() in modules, because that function immediately terminates the kernel. Use error() to report the error and then use the function return value to signal that a fatal error has occurred in the module. The kernel will then terminate in a cleaner way.

Compiling your module

There are two ways of compiling your module. In most cases you would want to use the first way. The second way described here requires up to date autotools installed on your machine and is recommended only if you would like to compile your module in the exactly the same way the official modules in the distribution are compiled.

The simple way

Create a subdirectory in the Tablix source tree, for example local/. Name the file with the source code of your module hello.c and save it into the subdirectory you've made. Now create a file named Makefile in that directory with the following contents (you must use tabs to indent lines!):


CFLAGS = -Wall -O2
INCLUDES = -I../src -I..

MODULES = hello.so

all: $(MODULES)

%.so: %.o
        gcc -shared -Wl,-soname,$@ -o $@ $< -lc
%.o: %.c
        gcc $(CFLAGS) $(INCLUDES) -c -o $@ $<

clean:
        -rm *.o *.so

install:
        cp $(MODULES) /usr/local/lib/tablix2

Later when you add more custom modules to your collection, you can add their names to the MODULES = line after hello.so. Just be sure to replace the .c extension with the .so.

Modules are usually installed in /usr/local/lib/tablix2, but that may be different on your system, depending on any --prefix options during Tablix configuration and / or your operating system. You can find the proper location by running the following command:


$ tablix -v

Fix the path in the install rule if necessary.

You can now compile and install your module with the following command:


$ make
$ make install

The autotools way

Note: This method requires a recent version of Autoconf, Automake and related software. This is the way the official modules in the distribution are compiled.

If you get any strange errors while running autoconf or automake, this 99% of cases means that you have the wrong version of autotools installed. Warnings about "underquoted definitions" are normal and can be ignored (they have nothing to do with Tablix).

Put the file with the source code of your module into the modules subdirectory. Now you have to edit the Makefile.am file. First add the following two lines at the end of the file


hello_la_SOURCES = hello.c
hello_la_LDFLAGS = -module -avoid-version

Then add hello.la to the end of the line beginning with pkglib_LTLIBRARIES.

Now you have to run autoconf and automake from the top directory and rerun ./configure to refresh the actual Makefiles. You can also run make -f Makefile.cvs if you are using the CVS version.

You can now compile and install your module with the following commands:


$ make
$ make install

Testing your module

First we need a Tablix configuration file that uses our new module. Because this simple modulus doesn't do anything, we can keep it as simple as possible. We only need to define one resource and one event to keep the kernel happy.


<?xml version="1.0" encoding="iso-8859-1"?>
<ttm version="0.2.0">
        <modules>
                <module name="hello.so" weight="10" mandatory="yes"/>
        </modules>

        <resources>
                <variable>
                        <resourcetype type="dummy-type">
                                <resource name="dummy-resource"/>
                        </resourcetype>
                </variable>
        </resources>

        <events>
                <event name="dummy-event" repeats="1"/>
        </events>
</ttm>

Save this configuration to a file named hello.xml and run Tablix. Because we used the debug() function in the module, you have to run Tablix with the -d5 option to see the "Hello world!" message.

A few screenfulls of messages will scroll past in the console as you run Tablix. Because hello.so module hasn't defined any fitness functions all timetables get fitness value 0 and Tablix will only run for a few hundred generations before declaring it has found the solution. Somewhere at the beginning of the output you should find the "Hello world!" message.


$ tablix2 -d5 hello.xml
TABLIX version 0.2.1, PGA general timetable solver
Copyright (C) 2002-2005 Tomaz Solc

[tablix] PGA using 4 nodes on 1 host
[tablix] maximum 1 node will do local search
[tablix] multicasting XML data
[tablix] Initializing nodes
...
[xxxxx] xmlsup: variable dummy-type: 1 resources
[xxxxx] xmlsup: Loading module /home/avian/software/lib/tablix2/hello.so
[xxxxx] xmlsup: Hello world!
[xxxxx] xmlsup: Weight: 10
[xxxxx] kernel: I have 1 tuples
...