This is part of the ChasmIntro series.
Follow these steps to go through the process of creating a fully (though not really useful) Chasm application. This should teach you about how to implement applications in Chasm.
Lets first set up an environment. In a terminal window, create a directory and move into it.
NOTE: You could also just use the chasm/tutorial/tut1_intro directory.
mkdir chasmtest ; cd chasmtest
Normally, a chasmxml file is created with the ChasmGui. For now, just create a file ChasmTest.chasmxml with the following text in it.
<CHASMConcept name="ChasmTest"> <state name="first">The first state.</state> <state name="second">The second state.</state> <symbol name="changing">Cause a change</symbol> <transition from="inited" to="first" by="changing"></transition> <transition from="first" to="second" by="changing"></transition> <transition from="second" to="second" by="changing"></transition> </CHASMConcept>
CHASM_HOME is an environment variable which is a list of directories which Concepts are searched for. It is ':' separated to allow multiple paths. A useful directory to use which holds many managed concepts is {Chasm install path}/include/chasm. The {Chasm install path} is the output of the chasmconfig binary with the --dir option and the include/chasm is the standard install directory. chasmconfig can then be used to postpend the version number. But this shouldn't be necessary as the chasm directory is a link to the latest chasm build.
All Concepts and Components in their definition give their name. This name can be something like "vocab/Button". So, for every entry in the CHASM_HOME environment variable, we look for vocab/Button.chasmxml and stop when we find the first one. You can just specify a fully resolved name but that is kinda sloppy.
This is useful for finding chasmxml files. Local Concept specs can overwrite ones in the main Chasm if you are tinkering with a local copy. Plus, you can have archives of Concepts which you have created and reused locally while still using the main chasm archive.
This is problematic however. Let me know your ideas/ concerns/ issues.
Chasm determines where to look for chasmxml files via the CHASM_HOME environment variable. This is determined by your install location. To see your install directory type 'chasm-config --dir' and append 'include/chasm_' and 'chasm-config -v'. So, for most installs, this would set CHASM_HOME to '/usr/local/include/chasm_0.3'. For multiple directories, use ':' to append new directories. So '/usr/local/include/chasm_0.3:.' searches the standard install directory and then the local directory. You need to make sure CHASM_HOME includes the current directory.
NOTE: in tcsh, type 'setenv CHASM_HOME /usr/local/incldue/chasm_0.3:.' NOTE2: A good idea is to set this in your .cshrc or .bashrc file so it is set every time you login.
A useful command in your .bashrc file would be (of course make sure chasm-config is in your PATH):
export CHASM_HOME=`chasm-config --dir`/include/chasm_`chasm-config -v`:.
Now that you have a chasmxml file and the CHASM_HOME variable set, you need the associated .C and .h files (i'll refer to these as the .[Ch] files). These are generated by chasm2C. Type the following:
chasm2C ChasmTest
NOTE: You may find supplemental files helpful for coding up concepts. Check them out and practice this example with them by adding the '-s' option as a command line parameter to chasm2C.
NOTE2: chasm2C by default generates extra comments in the code. If you want to skip this, add '-v' as a command line parameter.
You should now have ChasmTest.h, ChasmTest.C and ChasmTest.chasxml. There are lots of preprocessor defines, which you can read about here, in the .[Ch] files which are used to keep change parameteres based on the .chasxml file. For instance, CHASMCONCEPT refers to "ChasmTest" in this example because the name of the Concept is ChasmTest.
Chasm declares state entry methods named as 'do_<statename>' so for our states of first and second, we need to implement do_first and do_second. Append to the ChasmTest.C file the following state entry functions:
Symbol* CHASMCONCEPTBuilder::do_first(CO* _co) { printf("in first state\n"); return DIRECTOR->changing; }
Symbol* CHASMCONCEPTBuilder::do_second(CO* _co) { printf("in second state\n"); return NULL; }
NOTE: These are on the Builder class (named after the Builder design pattern) because the Builder class holds functionality which could be overwritten by later classes. The returned symbol changing is on the DIRECTOR class which is the ChasmTest? class. The symbol will not change even as the concept is extended through inheritance or other means. See ChasmBuilder for more on Builders..
Next, we create the .chasm.h and .chasm.C files (.chasm.[Ch]). They are the files after being processed by chasm and turned into actual code. DO NOT EDIT THESE AS THEY ARE OVERWRITTEN! To generate these file, type:
chasmprocess ChasmTest ChasmTest
The 1st argument is the chasmxml file and the 2nd is the file to parse. So, you can parse multiple files with a chasmxml file if you want. See ChasmBuilder
You need to create a file with main in it. Create a file called main.C and add the following code to it:
#include <chasm/CHASM.h>
#include "ChasmTest.chasm.h"
int main()
{
ChasmTest* ct;
ct = ChasmTest::cChasmTest();
ct->displayCurrentState();
CHASM::notifyConcept(ct->changing);
ct->displayCurrentState();
CHASM::notifyConcept(ct->changing);
ct->displayCurrentState();
return 0;
}
NOTE: Notice how I included ChasmTest.chasm.h and not ChasmTest.h. This is because ChasmTest.h has all the Chasm specific defines in it. NOTE2: See how ChasmTest is created with the static constructor. See more about that here.
Now, you just need to compile these files as you would with any other C/C++ file. In this case, type the following:
g++ main.C ChasmTest.chasm.C -o test -lCHASMbase -lcw -lxml2
NOTE: Add in your include and lib includes too. ex. -I{Chasm_Install_Path}/include and -L{Chasm_Install_Path}/lib where Chasm_Install_Path is the output of `chasm-config --dir`. The more general command then would be:
g++ main.C ChasmTest.chasm.C -I`chasm-config --dir`/include -L`chasm-config --dir`/lib -o test -lCHASMbase -lcw -lxml2
NOTE: For help creating Makefiles to check for changes and such, see here.
Just type './test'. The output should be as follows:
in state 'ChasmTest_2.inited' in first state in second state in state 'ChasmTest_2.second' in second state in state 'ChasmTest_2.second'
Notice how it prints the state it is in from the main.C file and then processes a symbol. In the state entry function for ChasmTest.first, it prints "in first state". Immediately, it goes to the second state. Why? Because the ChasmTest.first's entry function returns the changing symbol which is processed before returning. This causes the concept ChasmTest to enter its state second and print "in second state". The main.C file then prints that it is in the second state. We send another symbol and it reenters the second state. Lastly, main.C tells it to print its state again which is still in the second state.
NOTE: Why is it called ChasmTest_2? The '2' represents the Concept ID. See ChasmMetaInformation about CIDs.
There are three main ways of debugging applications in Chasm. Below are these ways applied to this tutorial. See here for more information.
add the following call to main.C after the static constructor for ChasmTest.
... ct->tracingOn(); ...

Notice how it tracks changes for ChasmTest in state due to every symbol that arrives and reports on which state it transfers out of and in to.
You can also turn on debugging for all concepts or turn off the color for terminals that don't handle color by using method calls in DebugCO.
The environment variable CHASMDEBUG is used to pass debugging statements using the functionality in cwlib. So, if you include the following lines at the start of main in main.C and add -DMODE_DEBUG to your compile command:
#include <cwlib/cwDebug.h>
...
readTraceFromEnv("CHASMDEBUG");
...
tprint1("tut1","Print this text\n");
...
When it runs, notice that there is nothing new. If you set CHASMDEBUG to "tut1", then it prints the text as:
Trace(tut1): main.C:19 (main) Print this text
This means "tut1" was called in file main.C line 19 function main. You could alternatively call it as:
traceprint(__FILE__,__LINE__,__func__,"tut1","Print this text\n");
... to skip having to specify the number of params on the command line.
Multiple options can be set with the ':' character dividing them. This is useful for instrumenting code with multiple types of debugging information which can be turned on and off easily and quickly by setting an environmental variable and not changing code and recompiling.
You can also use the chasm shell to debug at runtime using:
Chasm_initInteractiveShell();
This will catch a Ctrl-C in a running application to enter the shell (NOTE:gdb overrides this). For our testing here, place this command as the last line in main in main.C and place the include statement at the beginning of main.C:
#include <chasm/Shell.h> ... Chasm_enterInteractiveShell();
Type "help" at the prompt to list the commands and see here for more information.