Steps of Programming
Use IC_builder and IC_Compiler:
1. Use IC_Builder to design ICs (see below).
2. IC_Builder will generate fg format files and an input file (ic.dat)
for IC_Compiler. An example of ic.dat is here.
3. With ic.dat as input, IC_Compiler will automatically generate
app.h (fullly generated)
ic_func2.c (partially generated)
ic_func3.c (fullly generated)
actions.c (partially generated)
See Manual of IC_Compiler.
Design ICs:
1. One fg format file is needed for each type of IC, conventionally,
the names are something like "*.in".
2. Two ways to create a fg format file:
a. using IC Builder;
b. manually generating.
3. Each input message, output message, and action in fg format of an
ic type can be associated with parameters if you want. The format
is described in fg.format.
Make sure you are using the correct format.
For input message, only variable parameters are legal,
while for action and output message, variable, constant, and function
are all allowed as the parameter.
It is recommended to name a variable parameter prefixed with
capitals X, Y, Z, for example, X0, Y1, Z2. Note that the data type
of a variable parameter can not be specified in current version
of fg format.
The example of constant parameters:
I25|F1.2|Sfire
in which I25 is an interger 25, F1.2 means a float 1,2, and
Sfire stands for a string "fire".
The example of a function parameter is like G0(X).
See Section "Modify ic_func2.c" for the details of how to define
a function parameter.
No matter what data type a variable or constant parameter is,
the value of the parameter is maintained by IC_Manager with the
data type string.
In each transition of the fg format file,
the resolution of variable parameters of output message or action
is by name not by order.
For example,
parameters of input message: X1|X2|X3
parameters of output message or action: X1|X3|X2
The resolution is
X1 -- > X1
X3 -- > X3
X2 -- > X2
Write your own app.h: (can be automatically generated if using IC_Compiler).
1. app.h is used to define the necessary constants, like message types
and action types. In fg format files, ic_func2.c, ic_func3.c, and
actions.c, the constants defined here are heavily used.
Write your own and application dependent functions:
1. write action functions into actions.c
(can be automatically generated if use IC_Compiler).
Since the data type of a variable parameters can not be specified in
the current version, three functions are provided to get parameters
in action functions and converted parameters into the right data type
for computation.
int get_int_par(); /* get an integer parameter */
float get_float_par(); /* get a float parameter */
char *get_string_par(); /* get a string parameter */
Suppose in fg format file specifies an action function action1()
having four variable parameters:
105,X1|Y1|Y2|Y3 // 105 is action1(), four parameters:
// string variable X1, integer variable Y1,
// floating variable Y2, and integer variable Y3
Then in action1(), we should use
get_string_par(1, plist, active_ic),
get_int_par(2, plist, active_ic),
get_float_par(3, plist, active_ic), and
get_int_par(4, plist, active_ic)
to obtain the value of parameters X1, Y1, Y2, and Y3
in their right data type, respectively.
The user is responsible for using parameters of actions correctly.
2. modify the template functions in ic_func2.c
(can be partially generated if using IC_Compiler).
3. write decoding functions into ic_func3.c
(can be automatically generated if using IC_Compiler).
4. write the other application dependent functions
(it is recommended to write into app.c).
Modify ic_func2.c:
The template functions defined here are necessary for the success of
compilation. By reading the example given, you can find that most of
the functions are 'fill-in-blank' enumeratedly.
1. Fill the structure of internal output message by functions
fill_itype() and fill_content().
(Note fill_mflag() is not used in this version.)
If you do not specify the type of output IC in the fg format file,
you have to use fill_itype() to decide the type of output IC at run time.
Parameters of an output message are stored in the message content
when the output message is sent away. If you do not specify any parameter
associated with an output message, you can dynamically specify the content
of the output message by fill_content() at run time.
2. If you need to use internal memory inside ICs to save something,
please define it in the header file file mm.h. Then modify the template
functions init_mm() and dump_internal_mm().
3. If you need to save and restore internal memory of ICs into and from
a file (file.ic), modify the template functions save_mm() and restore_mm().
4. If you need to use predicate in fg format, you have to write your own
pred_match().
5. Define your variable- and function-type parameters by functions
userdef_f() and userdef_v():
sometimes a parameter for output messages or actions is not defined in
the input messages, this implies that this parameter is generated by some
internal computations. Therefore, the application is responsible to
define those parameters itself. Functions userdef_f() and userdef_v()
are designed to serve this purpose.
For example, there are 3 parameters X1, X2, and X3 associating
with input message: X1|X2|X3, while there are 3 parameters associating
with action or output message:
X1|Y1|G7(X2, Y2).
Since Y1 and Y2 are not in input message's parameters,
the user has to define Y1 and Y2 in function userdef_v().
Also, the user has to define the function G7 in userdef_f() in which
calls
get_int_par(1, plist, active_ic) and
get_float_par(2, plist, active_ic)
to get X2 and Y2 if they are integer and float, respectively.
Note: For different variable or functions, it is highly recommened
to use a different name for each variable or function.
If you use the same name, you have to distinguish them in userdef_v() or
userdef_f(). For example, if you use G7(X2,Y2) in action1 and
use G7(I10,Y1,Sfire) in action2. You have to distinguish these two G7
by the action # or whatever in userdef_f().
There are two default function-type parameters, namely G0 and H0 for INC
and DEC respectively. You can see how they are defined in userdef_f().
Before an output message with parameters is sent, the values
(in the data type string) of its parameters will be resolved automatically.
The user does not need to use functions get_int_par(), get_float_par(),
or get_string_par() to convert them into the right data type
because what we need is just to send out these parameters, not to use them
for computation.
In the sample ic_func2.c given, you can find examples regarding how to
define the parameters for both function- and variable-type parameters.
Partial code from ic_func2.c:
/* ---------------------------------------------------------------*/
/* userdef_f() and userdef_v() are new for the functionality of */
/* associated parameters in fg format. */
/* ---------------------------------------------------------------*/
char *userdef_f(func, plist, active_ic, msgtype)
/* If a function-type parameter is defined, the application should define
its own functions G1-G9 and H1-H9, whichever are used. The usage is
similar to that in do_actions(), i.e., the application has to plan the
usage of the parameters.
G0 is reserved as INC while H0 as DEC. The definitions for G0 and H0
also serve as examples.
*/
char *func;
para_list *plist;
active_index *active_ic;
int msgtype;
{
int i;
char *value;
if (strcmp(func, "G0") == 0) /* define G0(X) */
{
i=get_int_par(1, plist, active_ic);
i++;
value = (char *)malloc(10);
sprintf(value, "%d", i);
return value;
}
else if (strcmp(func, "H0") == 0) /* define H0(X) */
{
i=get_int_par(1, plist, active_ic);
i--;
value = (char *)malloc(10);
sprintf(value, "%d", i);
return value;
} else
return "NULL";
} /* userdef_f */
char *userdef_v(name, active_ic, msgtype)
/* If a variable-type parameter is defined for an action or an output
message, however is not defined in the input messages, the
application should define its own variables X0-X9, Y0-Y9, and Z0-Z9,
whichever are used. Examples of the usage are given here.
*/
char *name;
active_index *active_ic;
int msgtype;
{
char *tmp;
/* define Y1 which is not associated with
input message START_PREFETCH */
if ((strcmp(name, "Y1") == 0) && (msgtype == START_PREFETCH))
{
tmp = (char *)malloc(22);
sprintf(tmp, "%d", active_ic->id);
return tmp;
}
else /* default */
return "NULL";
} /* userdef_v */
Write your own driver.c:
1. driver.c is used to stimualte the IC_Manager by
external messages. It fills the structure of a
message based on the external message and then send the message to
the IC_Manager.
2. If you are implementing your active index system with WWW's cgi,
a cgi program main.c as the driver has been provided to you.
Use ic_state.c consisting of two main functions:
save_ix() saves the states of ic's in IX into file file.ic.
restore_ix() restores the states of ic's into IX from file file.ic.
By calling restore_ix() before calling ic_manager() and calling
save_ix() after calling ic_manager(), the previous states of ic's
in IX can be restored and the current states of ic's in IX can be
saved for next time's use.
Note: The storing and restoring of internal memory is not done as
it's application dependent. Application developers could
refer the style of file.ic to implement this part.
Some tips
a. Make sure to include <stdio.h>, <stdlib.h>, and <string.h> in your programs
to avoid unnecessary pointer problems.
b. Make sure that the number of parameters of a message is consistent.
For example, if you specify that the output message 5 has three parameters,
while in somewhere else you specify that the input message 5 has four
parameters, the program will crash for sure.
c. Make sure that the number of fields in the content of a message matches the
number of parameters specified in the fg format file(s) for that type of
message.
For example, if in the fg format file:
3,X0|X1|X2 // input message 3 has 3 parameters
and in the program, the content is "100|HELP", then the program will crash
as it tries to retrieve a value for X2.
Note: The vertical bar is the delimiter for both the fields of a content and
the parameters specified in fg format. Therefore, there should not be
a vertical bar in the content of a message.
d. If you want to dynamically decide the type of output message at run time,
you can send an output message to the ic itself which uses predicates to
decide which transition will be triggered and thus will decide what type
of output message will be sent.
e. We often want to send a message to an ic that we have sent
a message earlier. This can be implemented by a system-supplied
function same_ic(msg) which will return the ic-id that we have sent the
message msg most recently. The function same_ic(msg) will return
an error flag -1 if it cannot find a live ic that has received msg earlier.
Only the most recent ic is returned, because there could be multiple
ic's receiving a message, or multiple messages sent to the same ic.
An alternative is to ask the receiving IC to send back its id. Therefore,
the sending IC can know which IC got the message it just sent.