A Hands-On Guide to the Common Component Architecture
Common Component Architecture Forum Tutorial
Working Group
Version: 0.7.0
Copyright © Common Component Architecture Forum
This document is distributed under the Creative Commons Attribution 2.5 License. See http://creativecommons.org/licenses/by/2.5/legalcodefor the complete license agreement.
In summary, you are free:
Under the following conditions:
Your fair use and other rights are in no way affected by the above.
Common Component Architecture Forum Tutorial Working Group, A Hands-On Guide to the Common Component Architecture, version 0.7.0, http://www.cca-forum.org/tutorials/.
Or in BIBTEX format:
| @Manual{cca-tutorial:0.7.0, | |||
| title | = | {A Hands-On Guide to the Common Component Architecture}, | |
| author | = | {Common Component Architecture Forum Tutorial Working Group}, | |
| edition | = | {0.7.0}, | |
| year | = | , | |
| URL | = | {http://www.cca-forum.org/tutorials/} | |
| } | |||
The Common Component Architecture (CCA) is an environment for component-based software engineering (CBSE) specifically designed to meet the needs of high-performance scientific computing. It has been developed by members of the Common Component Architecture Forum .
This document is intended to guide the reader through a series of increasingly complex tasks starting from composing and running a simple scientific application using pre-installed CCA components and tools, to writing (simple) components of your own. It was originally designed and used to guide the `hands-on' portion of the CCA tutorial, but we hope that it will be useful for self-study as well.
We assume that you've had an introduction to the terminology and concepts of CBSE and the CCA in particular. If not, we recommend you peruse a recent version of the CCA tutorial presentations before undertaking to complete the tasks in this Guide.
If you find errors in this document, or have trouble understanding any portion of it, please let us know so that we can improve the next release. Email us at cca-tutorial@cca-forum.org with your comments and questions.
The hands-on exercises and this Guide are evolving and improving. We will maintain links to the current releases of this Guide, the tutorial code, and accompanying tools at http://www.cca-forum.org/tutorials/#sources . If you want older versions or intermediate "release candidates", follow the links there to the parent download directories to see the full list of available files.
| $ | This style |
Throughout this Guide, we refer to various files and directories, the precise location of which depends on how and where things were built and installed. All such references will be based on a few key directory locations, which will be determined when you build and install the software (Appendix E and Appendix F). Wherever appropriate, we will write these as environment variables, so that the text in the Guide can simply be pasted into your shell session (assuming your login environment is setup as suggested in Section E.4).
| Warning Note that tools such as the Ccaffeine framework do not expand environment variables. In these cases, you'll need to type in the complete path, substituting the placeholder (i.e., ``TUTORIAL_SRC'') with the actual path. |
If you're participating in an organized tutorial, you will be given information separately about the particular paths corresponding to these locations.
0 [TAU_ROOT ($TAU_ROOT)] The installation location of the TAU Portable Profiling package. (See Section E.3.)
[TAU_CMPT_ROOT ($TAU_CMPT_ROOT)] The installation location of the TAU performance component. (See Section E.3.)
| Warning If you're part of an organized tutorial please be careful to use the WORKDIR you are assigned! Often there are special considerations in such an environment, which might not be obvious to you as a participant. For example, it is fairly common for all cluster nodes to mount user home directories from a single NFS file server. An entire class of students working on I/O-intensive activities (like building the tutorial code) at the same time has been known to kill servers from time to time. So frequently, you will be asked to use directories local to your assigned cluster node. |
There are quite a few people active in the Tutorial Working Group who have contributed to the general development of the CCA tutorial and this Guide in particular:
Finally, we must acknowledge the efforts of the numerous additional people who have worked very hard to make the Common Component Architecture what it is today. Without them, we wouldn't have anything to present tutorials about!
In this Guide, we will take you step by step through a series of hands-on tasks with CCA components in the CCA software environment. The initial set of exercises are based on an example that's intentionally chosen to be very simple from a scientific viewpoint, numerical integration in one dimension, so that we can focus on the issues of the component environment. It may look like overkill to have broken down such a simple task into multiple components, but once you have a basic understanding of how to use and create components, you should be able to extend the concepts to components that are scientifically interesting to you and far more complex.
The exercises are laid out as follows:
0 In Chapter 5, you will use the TAU performance observation tool to automatically instrument a component interface and monitor the performance of the application.
You are strongly advised to at least read and understand Chapter 2 before going on to later exercises. You'll need to use the techniques of Chapter 2 to test the components you write later.
In Chapter 2, you'll be working with a complete version, pre-built of the tutorial code tree. Then in Chapter 3 you'll start from scratch to create components on your own, replicating those in Chapter 2. In this way, the separate complete tutorial code tree can always serve as a reference if you run into problems. Of course if you're working through this Guide as part of an organized tutorial, there should be instructors around who can help you. And if you're working on your own, you can email us for help at cca-tutorial@cca-forum.org .
| Tip Some of these exercises can involve a fair amount of typing. You may find it convenient to use the online HTML version of this Guide (at http://www.cca-forum.org/tutorials/#sources ) to cut and paste the necessary inputs. Note, however, that not everything can be cut-and-pasted directly. Take particular care with lines that had to be broken for purposes of documentation, and for placeholder values such as ``TUTORIAL_SRC''. |
The CCA is, at its heart, just a specification. There are several realizations of the CCA as a software environment. In this Guide, we use the following tools to provide that software environment, which are currently the most widely used for high-performance (as opposed to distributed) computing using the CCA:
Before starting the exercises, you'll need to do a little bit of work to set things up. Depending on whether you're working through the Guide on your own (see Section 1.2.1) or participating in an organized tutorial (see Section 1.2.2), this may include getting logged in to a remote system, preparing the CCA environment, and building the tutorial code. Once you've setup everything as outlined below, you should be ready to proceed to Chapter 2.
If you're participating in an organized tutorial, most of the preparatory work will have been done in advance by the tutorial instructors. Usually, they will provide you with a separate set of instructions tailored to the arrangements for the particular tutorial you're attending.
| Tip In some of the later exercises (starting with Section 4), you will need to build your own copy of (parts of) the tutorial code tree so that you can modify them. Depending on your hardware environment, this may be pretty time consuming. If you're pressed for time you may want to go ahead and start building your own copy of the PDE and then ODE portions of the tutorial code tree before starting the first exercise. Follow the instructions in Appendix F, and you probably want to launch the build in a separate window. The tutorial instructors should be able to tell you whether this ``pre-build'' step is necessary. |
In this exercise, you will work with pre-built components from the integrator example to compose several CCA-based applications and execute them. The integrator application is a simple example, designed to illustrate the basics of creating, building, and running component-based applications without scientific complexities a more realistic application would also present. The purpose of this application is to numerically integrate a one-dimensional function. Several different integrators and functions are available, in the form of components. A ``driver'' component controls the calculation, and for the Monte Carlo integrator, a random number generator is also required. The specific components available are shown in Table 2.1.
| Components | Notes | |
| Drivers | <#10212#> | |
| drivers.CXXDriver | ||
| drivers.F90Driver | ||
| drivers.PYDriver | ||
| Integrators | Various integration algorithms | |
| integrators.Boole | ||
| integrators.MonteCarlo | ||
| integrators.Midpoint | ||
| integrators.Simpson | ||
| integrators.Simpson38 | ||
| integrators.Trapezoid | ||
| Functions | ||
| functions.CosFunction | cos(x)
; integrates to
sin(1) |
|
| functions.CubeFunction | x3 ; integrates to 0.25 | |
| functions.LinearFunction | x ; integrates to 0.5 | |
| functions.PiFunction |
|
|
| functions.QuinticFunction | x5 -4x4
; integrates to
|
|
| functions.SquareFunction | x2
; integrates to
|
|
| Random Number Generators | ||
| randomgens.RandNumGenerator | Required for Monte Carlo integration | |
The Ccaffeine framework provide three different ways for users to interact with it in order to assemble and run CCA applications. You can type commands in yourself at the framework's prompt, execute a script containing those same commands, or use a graphical user interface.2.1The graphical approach is the easiest for most people to get a feel for how components work, so we will start with that (Section 2.1) and later discuss how actions in the GUI map onto instructions in a script (see Section 2.2).
In practice, most users set the GUI interface aside after they become more comfortable with the CCA environment in favor of the scripting approach. That's especially true once they've developed a bunch of components and want to run simulations with them in batch jobs, where GUIs tend not to be so convenient. Of course it is entirely up to you which approach you use in the long run.
| Note At this point, you will start using the tutorial-src code tree. If you're doing this tutorial as a self-study exercise, you'll need to make sure it has been built according to the instructions in Appendix F. For organized tutorials, this is generally done in advance by the tutorial instructors. |
There is a graphical front-end for Ccaffeine (known as Ccaffeine GUI , or ``the GUI'' which provides a fairly simple visual programming metaphor for the assembly of applications using CCA components. In this exercise, we'll use the Ccaffeine GUI to assemble and run several different ``applications'' using the components already available in the tutorial-src tree.
Ccaffeine and its GUI are run as two separate processes, possibly on two different machines. Depending on the specific circumstances, there are a variety of ways to invoke the GUI and the Ccaffeine framework. Bocca generates two helper scripts in the project's utils subdirectory, which will serve most purposes. Which to use depends on whether the graphical display you're using (the ``GUI host '') is directly attached to the machine on which you're running the framework (the ``Ccaffeine host ''), or whether they're separated by a network link.
When you're working on a display that is directly attached to the Ccaffeine host , the Bocca -generated utils/run-gui.sh script is the simplest one to use. It requires no arguments, launches both Ccaffeine and the GUI, and automatically initializes the framework with a palette consisting of all of the components in the Bocca project.
| Tip Always make sure you're using the run-gui.sh script generated for the particular project you're working on because they'll be initialized with different sets of components. In this chapter, the command is $TUTORIAL_SRC/utils/run-gui.sh, but in later exercises it will be different! |
There are two different mechanisms to run the GUI remotely:
| Tip Connections between the GUI and the framework can be tunneled through an ssh connection. This may help in cases where firewalls or other network setups that prevent direct point to point connections (i.e. cluster compute nodes accessible only through the head node). See Appendix D and in particular Section D.3. |
| Note This procedure requires that you have GUI on your GUI host . This includes the simple-gui.sh (on Windows, simple-gui.bat) script and the ccafe-gui.jar file. In a normal build of the CCA tools, you can find these files in the $CCA_TOOLS_ROOT/lib directory. If you're participating ain an organized tutorial, they also have been made available on a web site or other more convenient location. No special installation procedure is required, but the script and the jar file do need to be in the same directory. |
The port number must be in the range 1025-65535, and cannot be in use by another application on the host (if it is, you will get an error message; simply try another port). You must use the same port number for the framework and the GUI. In organized tutorials, you may be assigned a port to help reduce the chances of collisions. If so, please use it!
| Tip Always make sure you're using the bocca-gui-backend.sh script generated for the particular project you're working on because they'll be initialized with different sets of components. In this chapter, the command is $TUTORIAL_SRC/utils/bocca-gui-backend.sh, but in later exercises it will be different! |
| Tip If you invoke the simple-gui.sh (simple-gui.bat) script without arguments, the GUI will pop up a dialog box asking you to specify the hostname and port number to connect to. Filling in these dialogs quickly gets tedious, so you're better off using the command line. (In Windows, launch a Command Prompt window, and change directories to wherever you put simple-gui.bat and the GUI jar file.) In both Windows and most Linux/unix shells, you can simply use the |
| Tip We have on occasion observed problems with the Ccaffeine GUI interface hanging (most often while populating the palette as the GUI starts up). We think this is due to a subtle race condition in the GUI, which we haven't yet been able to isolate. The best advice seems to be to kill both the framework and GUI and try launching them again. |
As your usage of the CCA becomes more sophisticated, you're likely to encounter situations where the Bocca -generated helper scripts don't do exactly what you want. For example, you may need to use a different rc file to initialize the framework. Is is therefore worth mentioning a couple of the underlying tools, which are part of the CCA tools distribution:
For the purposes of this exercise, we will assume that you are working in and environment in which GUI host and Ccaffeine host are separate machines. If they are the same, you can use $TUTORIAL_SRC/utils/run-gui.sh as described in Section 2.1.1 instead of the first two steps, below.
In the Ccaffeine host terminal window, you will see something like:
my rank: -1, my pid: 9625 Type: Server
Run simple-gui.sh --port port_num --host backend_host (on Windows, simple-gui.bat) on the GUI host .
Once the GUI connects to Ccaffeine , Ccaffeine begins running the rc file it was invoked with. In the GUI host terminal window, you first see some startup messages from the GUI itself, followed by a series of messages as Ccaffeine processes the rc file and the GUI displays the results. These are debugging messages and can largely be ignored.
In the Ccaffeine host terminal, you should see some additional messages as Ccaffeine processes the rc file, like:
CCAFFEINE configured with spec (0.8.2) and babel (1.0.4). CCAFFEINE configured with classic (0.5.7). CCAFFEINE configured without neo and neo components. CmdLineClient parsing ... CmdContextCCA::initRC: Found components/tests/test_gui_rc. # There are allegedly 11 classes in the component path
Finally, in the GUI host window, you should see some output associated with the GUI's initialization process, and the GUI itself should have appeared on your display, looking something Figure 2.1.
| Tip The default layout has the palette area fairly narrow. You can click-and-drag on the bar separating the palette and the arena to adjust the width. |
| Note You may see additional components in your palette , as we try to expand the variety of examples we provide in the tutorial-src . |
As mentioned above, the test_gui_rc sets up the path and loads the framework's palette with a set of available components. rc files are explained in detail in Section 2.2.
| Tip You can drag components around the arena to arrange them as suits you - just click on the black area of the component and drag it to the new location. The positions have no bearing on the operation of the GUI or your application. |
| Tip If you hover the cursor over a particular port on a component, a ``tool tip'' box will pop up with the port's name and type based on the arguments to the addProvidesPort or registerUsesPort calls in the component's setServices method. This can be useful for double checking to make sure you're connecting matching ports. Also notice that when you hover over a particular port (either uses or provides ), matching ports of the opposite type (either provides or uses ) will be highlighted. |
| Note You can move components around even after their ports are connected - the connections will automatically rearrange. There is no harm in connections crossing each other, nor in connections passing behind other components (though of course they may make it harder to interpret the ``wiring diagram'' correctly). |
At this point, your GUI should look something like Figure 2.4.
The application is now fully assembled and is ready to run. If you
click-and-release the go button on the CXXDriver0
component, you should see the result appear in the Ccaffeine host
terminal, ``Value = 3.139160''2.2 and the
message
``IN: ##specific go command successful''
in the GUI host terminal.
| Tip Remember that your application is running within the framework. Unless the application itself does something special, the output from the application will appear in the window in which the framework is running. |
| Tip As we've mentioned, wiring diagrams can become hard to interpret when they become cluttered, as is the case with the screen shot above. To help interpret the diagram, remember the following:
|
There is no need to remove unused components from the arena - as long as they are not connected to active components, they will not interfere.2.4 In fact in this case, neither MonteCarlo0 nor RandNumGenerator0 are used, so it is safe to leave them connected to each other.
| Note Steps 8 and 9 could have been done in either order. |
Click-and-release the go button on the CXXDriver0 component,
you should see the result appear in the Ccaffeine host terminal,
``Value = 3.141593'' and the message
``IN: ##specific go command successful'' in the GUI host
terminal.
Value = 0.250000''2.5 in the Ccaffeine host
terminal.
| Note Observe that as a user of CCA components, you have no idea what language each component is implemented in. (Admittedly, the names of the drivers are suggestive of the implementation language, but those names were chosen at the convenience of the component developer, and they provide no guarantees regarding the components' implementations.) The language interoperability features of Babel allow components to be hooked together regardless of implementation language with complete transparency. |
| Tip If you've used the GUI to setup and start a long-running simulation, and you don't want to leave the GUI running continuously, you can use the File |
| Tip If the backend crashes while the GUI is running, exit the GUI by using Detach. Trying to Quit without a running backend will cause the GUI to hang. |
In practice, most people don't use the GUI all the time. And even die-hard GUI users will sometimes need to modify the rc file that does the initialization. Ccaffeine will also accept commands interactively or in the form of a script (the rc file). This capability is very useful when you simply want to run CCA-based applications that you already know how to assemble. In this section, we will examine in detail an rc file that does everything you did in the GUI in the previous section.
When we're not using the GUI, the Ccaffeine invocation is much simpler, and there is no need for the helper scripts we used before (utils/bocca-gui-backend.sh or gui-backend.sh). For direct use, Ccaffeine can be invoked as ccafe-single or ccafe-batch, depending on whether you're using it in a single-process (i.e. sequential) interactive situation, or in non-interactive situations, including parallel jobs. These commands are part of the CCA tools installation, and should be in your path if you've followed the procedures in Appendix E.4.
Change directories to your WORKDIR so that we can capture the output of running the $TUTORIAL_SRC/components/tests/task0.rc rc file.
Execute the command
| $ |
ccafe-single
\
--ccafe-rc $TUTORIAL_SRC/components/tests/task0.rc \ > task0.out 2>&1 |
(if you're using the csh or tcsh shell, the output redirection should be `` >& task0.out'' instead of `` > task0.out 2>&1'').
View the task0.out file satisfy yourself that the script ran. (Of course you can view the script itself too, if you want.) Below we'll work our way through each section of the script and the corresponding output, but it may help you to see the input and output in their entirety. The step numbers appearing in the script comments should correspond to the steps in the preceding GUI procedure.
#!ccaffeine bootstrap file. # ------- don't change anything ABOVE this line.------------- # Step 2 path path set /home/csm/bernhold/proj/cca/tutorial/tutorial/src-acts07/components/lib path palette repository get-global drivers.CXXDriver repository get-global drivers.F90Driver repository get-global functions.CubeFunction repository get-global functions.LinearFunction repository get-global functions.QuinticFunction repository get-global functions.SquareFunction repository get-global integrators.MonteCarlo repository get-global integrators.Simpson repository get-global integrators.Trapezoid repository get-global randomgens.RandNumGenerator palette
The rc file begins with a ``magic'' line (a structured comment) indicating that the script is meant to be processed by Ccaffeine . Ccaffeine expect to find such a line at the beginning of all rc files.
Ccaffeine uses a ``path'' to determine where it should look for CCA components (specifically the .cca files, which internally point to the actual libraries that comprise the component). The rc file prints the path before and after setting the path for pedagogical reasons. In ``real'' scripts, you might want to print the path out for debugging or documentation purposes.
Path-related commands in Ccaffeine include:
As you saw in the GUI, Ccaffeine has the concept of a palette of components from which applications can be assembled. Unlike a typical unix shell, where putting an executable into your path means you can use it directly, Ccaffeine has a two step process. Components in the path can be added to the palette using the command repository get-global class_name, where class_name is the component's class name. This two step approach gives you a little more control when there are large numbers of components in your path. However in this case, we've simply loaded all of the components in the tutorial-src tree.
The palette commands before and after the block of repository commands is simply meant to illustrate that the framework's palette starts empty, and ends up with the components you requested. They aren't needed in a ``real'' script.
The output from these commands should look something like this:
CCAFFEINE configured with spec (0.8.2) and babel (1.0.4). CCAFFEINE configured with classic (0.5.7). CCAFFEINE configured without neo and neo components. my rank: -1, my pid: 27566 Type: One Processor Interactive CmdContextCCA::initRC: Found task0_rc. pathBegin pathEnd! empty path. # There are allegedly 11 classes in the component path pathBegin pathElement /home/csm/bernhold/proj/cca/tutorial/tutorial/src-acts07/components/lib pathEnd Components available: Loaded drivers.CXXDriver NOW GLOBAL . Loaded drivers.F90Driver NOW GLOBAL . Loaded functions.CubeFunction NOW GLOBAL . Loaded functions.LinearFunction NOW GLOBAL . Loaded functions.QuinticFunction NOW GLOBAL . Loaded functions.SquareFunction NOW GLOBAL . Loaded integrators.MonteCarlo NOW GLOBAL . Loaded integrators.Simpson NOW GLOBAL . Loaded integrators.Trapezoid NOW GLOBAL . Loaded randomgens.RandNumGenerator NOW GLOBAL . Components available: drivers.CXXDriver drivers.F90Driver functions.CubeFunction functions.LinearFunction functions.QuinticFunction functions.SquareFunction integrators.MonteCarlo integrators.Simpson integrators.Trapezoid randomgens.RandNumGenerator
| Note rc files used to initialize the GUI should contain only the magic line, path and repository get-global commands. You can view $TUTORIAL_SRC/components/tests/guitest.gen.rc as an example. |
# Steps 3-4 instances instantiate drivers.CXXDriver CXXDriver0 instantiate functions.PiFunction PiFunction0 instantiate integrators.MonteCarlo MonteCarlo0 instantiate randomgens.RandNumGenerator RandNumGenerator0 instances
The command syntax is instantiate class_name instance_name. (The plain instantiate commands before and after are, once again, for pedagogical purposes, to list the contents of the arena .) The component's class_name is set in the SIDL file where it is defined, and is also used in the repository get-global command. The instance_name is chosen by the user, and must simply be unique within the arena . You may remember that the GUI suggests a default instance_name when prompting you for it, but that's a feature of the GUI, not the framework. Here you have to enter it yourself. It happens that we've used the same thing that the GUI would suggest.
The output from these commands should look something like this:
FRAMEWORK of type Ccaffeine-Support CXXDriver0 of type drivers.CXXDriver successfully instantiated PiFunction0 of type functions.PiFunction successfully instantiated MonteCarlo0 of type integrators.MonteCarlo successfully instantiated RandNumGenerator0 of type randomgens.RandNumGenerator successfully instantiated CXXDriver0 of type drivers.CXXDriver FRAMEWORK of type Ccaffeine-Support MonteCarlo0 of type integrators.MonteCarlo PiFunction0 of type functions.PiFunction RandNumGenerator0 of type randomgens.RandNumGenerator
# Steps 5-6 display chain display component MonteCarlo0 connect CXXDriver0 integrate MonteCarlo0 integrate connect MonteCarlo0 function PiFunction0 function connect MonteCarlo0 RandomGeneratorPort RandNumGenerator0 RandomGeneratorPort display chain
The command syntax is connect user_component user_port provider_component provider_port.
The display command provides various kinds of information about the arena and components therein. display chain details the connections between components. display component component_instance lists the uses and provides ports the component has registered.
The output from these commands should look something like this:
Component CXXDriver0 of type drivers.CXXDriver Component FRAMEWORK of type Ccaffeine-Support Component MonteCarlo0 of type integrators.MonteCarlo Component PiFunction0 of type functions.PiFunction Component RandNumGenerator0 of type randomgens.RandNumGenerator ------------------------------------ Instance name: MonteCarlo0 Class name: integrators.MonteCarlo ------------------------------------ UsesPorts registered for MonteCarlo0 0. Instance Name: function Class Name: function.FunctionPort 1. Instance Name: RandomGeneratorPort Class Name: randomgen.RandomGeneratorPort ------------------------------------ ProvidesPorts registered for MonteCarlo0 Instance Name: integrate Class Name: integrator.IntegratorPort ------------------------------------ CXXDriver0))))integrate---->integrate((((MonteCarlo0 connection made successfully MonteCarlo0))))function---->function((((PiFunction0 connection made successfully MonteCarlo0))))RandomGeneratorPort---->RandomGeneratorPort((((RandNumGenerator0 connection made successfully Component CXXDriver0 of type drivers.CXXDriver is using integrate connected to Port: integrate provided by component MonteCarlo0 Component FRAMEWORK of type Ccaffeine-Support Component MonteCarlo0 of type integrators.MonteCarlo is using function connected to Port: function provided by component PiFunction0 is using RandomGeneratorPort connected to Port: RandomGeneratorPort provided by component RandNumGenerator0 Component PiFunction0 of type functions.PiFunction Component RandNumGenerator0 of type randomgens.RandNumGenerator
# Step 7 go CXXDriver0 go
The command syntax is go component_instance port_name.
The output from these commands should look something like this:
Value = 3.140205 ##specific go command successful
# Step 8 instantiate integrators.Simpson Simpson0 instantiate functions.CubeFunction CubeFunction0 # Step 9 disconnect CXXDriver0 integrate MonteCarlo0 integrate disconnect MonteCarlo0 function PiFunction0 function # Step 10 connect CXXDriver0 integrate Simpson0 integrate connect Simpson0 function PiFunction0 function display chain go CXXDriver0 go # Step 11 disconnect Simpson0 function PiFunction0 function connect Simpson0 function CubeFunction0 function display chain go CXXDriver0 go
The output from these commands should look something like this:
Simpson0 of type integrators.Simpson successfully instantiated CubeFunction0 of type functions.CubeFunction successfully instantiated CXXDriver0))))integrate-\ \-integrate((((MonteCarlo0 connection broken successfully MonteCarlo0))))function-\ \-function((((PiFunction0 connection broken successfully CXXDriver0))))integrate---->integrate((((Simpson0 connection made successfully Simpson0))))function---->function((((PiFunction0 connection made successfully Component CXXDriver0 of type drivers.CXXDriver is using integrate connected to Port: integrate provided by component Simpson0 Component CubeFunction0 of type functions.CubeFunction Component FRAMEWORK of type Ccaffeine-Support Component MonteCarlo0 of type integrators.MonteCarlo is using RandomGeneratorPort connected to Port: RandomGeneratorPort provided by component RandNumGenerator0 Component PiFunction0 of type functions.PiFunction Component RandNumGenerator0 of type randomgens.RandNumGenerator Component Simpson0 of type integrators.Simpson is using function connected to Port: function provided by component PiFunction0 Value = 3.141593 ##specific go command successful Simpson0))))function-\ \-function((((PiFunction0 connection broken successfully Simpson0))))function---->function((((CubeFunction0 connection made successfully Component CXXDriver0 of type drivers.CXXDriver is using integrate connected to Port: integrate provided by component Simpson0 Component CubeFunction0 of type functions.CubeFunction Component FRAMEWORK of type Ccaffeine-Support Component MonteCarlo0 of type integrators.MonteCarlo is using RandomGeneratorPort connected to Port: RandomGeneratorPort provided by component RandNumGenerator0 Component PiFunction0 of type functions.PiFunction Component RandNumGenerator0 of type randomgens.RandNumGenerator Component Simpson0 of type integrators.Simpson is using function connected to Port: function provided by component CubeFunction0 Value = 0.250000 ##specific go command successful
# Step 13 quit
The output from these commands should look something like this:
bye! exit
| Warning If your rc file ends without a quit command, Ccaffeine will leave you in interactive mode rather than terminating and returning you to the shell prompt. If you make this mistake a Control-c will interrupt Ccaffeine and return you to the shell prompt. |
Feel free to copy $TUTORIAL_SRC/components/tests/task0.rc to your workspace, modify it, and run it yourself.
There are a couple of other features of the GUI and its interaction with the Ccaffeine backend that are worth mentioning.
The syntax of the bld file is similar to that of the rc file, but they are not interchangeable. The bld file can contain commands to instantiate and destroy components and to connect and disconnect ports, as well as commands to move components within the arena , and it can only be interpreted by the GUI. The path and repository get-global commands must always be in the rc file, which is interpreted only by the Ccaffeine backend. Also, Ccaffeine itself does not understand the movement commands of the bld file.
While the CCA specification allows you to create components ``by hand'', it is much quicker to use an application generator that provides templated code for components and a build system. Naturally Bocca cannot create your implementation for you, but all of the glue code for multi-language interoperability and component interfaces in a CCA application is created and maintained with a few commands. The advantage of this approach is that a lot of build and component defaults have been chosen for you. The downside is that, while some customization is possible, the project directory and file structures are largely predetermined.
If your CCA environment is configured properly
(Section E) then the bocca command
is already in your command path and you are ready to go. Find a safe
place to begin your Bocca project, such as your WORKDIR:
| $ | cd $WORKDIR |
The first thing to do is to create a project directory within which
all of your components and ports will reside. Normally you would
choose a relevant project name but for now we will just call it
demo. Create the project directory now:
| $ | bocca create project demo --language=LANG |
The project was created successfully in /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo
Here LANG specifies the default implementation language for components in this project, if you don't specifically indicate a language when creating the component (a project can contain components in any mixture of Babel -supported languages). For this exercise, choose the one of c, cxx, f90, java, or python with which you are most comfortable (some of these choices may not be available if your Babel installation is not configured for them, but these are the languages for which this Guide has detailed instructions). If you don't specify a default language when creating the project, Bocca will use C++.
Now that the project is created, we see that Bocca has created a lot of build scaffolding to support the componentized application we will write. The first thing you notice is that Bocca has created a directory:
| $ | ls -F |
demo/
Feel free to poke around a bit:
| $ | ls -F demo |
BOCCA/ buildutils/ components/ config/ configure* configure.ac* configure.ac1 depl/ external/ install/ Makefile make.project make.project.in make.rules.user make.vars.user ports/ README utils/
Before using a new Bocca project or working with an existing project just checked out from a source code repository, you will need to configure it for the details of your local environment. For a new project this is easy: ./configure from within your new project directory.
| $ | cd demo && ./configure |
checking for bash... /bin/sh checking for GNU make... make checking for gcc... /usr/bin/cc checking for C compiler default output file name... a.out checking whether the C compiler works... yes checking whether we are cross compiling... no checking for suffix of executables... checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether /usr/bin/cc accepts -g... yes checking for /usr/bin/cc option to accept ISO C89... none needed checking for openpty in -lutil... yes checking for bocca... /project/projectdirs/cca/install-new/bin/bocca c cxx f90 f77 python configure: Configuring with languages: c cxx f90 f77 python configure: Project source dir apparently /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo configure: Using 4 processe(s) in calls to make. checking whether make sets $(MAKE)... yes configure: creating ./config.status config.status: creating make.project config.status: creating buildutils/make.vars.common config.status: creating utils/run-gui.sh config.status: creating utils/bocca-gui-backend.sh config.status: creating utils/demo-config config.status: creating utils/config-data config.status: creating utils/demo-config.h config.status: executing outmsg commands
Let's create a component. First make sure that your current working directory is inside the project directory:
| $ | pwd |
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo
It is important to be in the project directory (or its subdirectories) when you invoke bocca because it picks up all of the context for your project from there (similar to CVS or Subversion). Go ahead and create the component now:
| $ | bocca create component emptyComponent |
Babel updating the cxx implementation of component demo.emptyComponent ...
Notice that Bocca selected demo as the default package name for emptyComponent since no package name was specified when creating the component. Normally, Bocca uses the project name as the package name for both ports and components unless a different default package name was specified when the project was created. We have named our component emptyComponent because it has no uses nor provides ports and thus is rather uninteresting. Nonetheless, Bocca has generated all of the necessary make system scaffolding and code for the component, including the setServicescall. Listing the directory shows the files Bocca has generated (shown here for LANG = cxx):
| $ | ls components/demo.emptyComponent |
BOCCA demo_emptyComponent_Impl.cxx demo_emptyComponent_Impl.hxx demo_emptyComponent_Impl.hxx.rej glue Makefile make.rules.user make.vars.user
Components created in Fortran, C, and Python will contain a similar set files appropriate to the language. In the components directory a new directory, demo.emptyComponent, has been created to hold your component. And inside there is the code already generated for the component (again continuing with LANG = cxx) in the files: demo_emptyComponent_Impl.cxx, demo_emptyComponent_Impl.hxx with some Babel glue code in the glue subdirectory. Note the file ending in .rej named demo_emptyComponent_Impl.hxx.rej. This file is produced by the Bocca splicing process. It records code fragments that Bocca discarded while generating demo_emptyComponent_Impl.hxx and can usually be ignored and even deleted.
Although the component you've created can't actually do anything useful at this point, it is a valid component. You can build it and instantiate it in Ccaffeine if you like:
| $ | make |
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # No SIDL files in ports/sidl, skipping build for ports # ======================================================================= # ======================================================================= # Building in components/clients/, languages: cxx # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: cxx # ======================================================================= [s] Building class/component demo.emptyComponent: [s] using Babel to generate cxx implementation code from demo.emptyComponent.sidl... [s] compiling sources... [s] creating class/component library: libdemo.emptyComponent.la ... [s] finished libtooling: components/demo.emptyComponent/libdemo.emptyComponent.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.emptyComponent_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... Build summary: SUCCESS building demo.emptyComponent ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
(Your output should be substantially similar, but will at least have different paths.)
Now, you can run Ccaffeine and the GUI following the same procedure you used in Section 2.1. If you instantiate the emptyComponent , you should see something similar to Figure 3.1. Of course it lacks any uses or provides ports and thus cannot be used for anything, but it is a full-fledged CCA component.
In order to have some exportable or importable functionality in a component we must have some uses and provides ports. Bocca will also create the scaffolding and code for ports. Following the model of the integrator application of Chapter 2, we will create a Function , an Integrator , and a Driver . However before we can do that we will have to create some ports for these components to use and provide .
Let's begin by creating a FunctionPort and an Integration :
| $ | bocca create port Integration |
Updating makefiles (for demo.Integration)...
| $ | bocca create port FunctionPort |
Updating makefiles (for demo.FunctionPort)...
Notice that we are continuing to use the default package demo, though we could specify something different.
Now, create a set of components similar to those that you used in Chapter 2, specifying that they will provide or use the appropriate ports:
| $ | bocca create component Function --provides=FunctionPort@fun |
Babel updating the cxx implementation of component demo.Function ...
| $ |
bocca create component Integrator
\
--provides=Integration@integrate \ --uses=FunctionPort@odeRHS |
Babel updating the cxx implementation of component demo.Integrator ...
| $ |
bocca create component Driver --go=run
\
--uses=Integration@integrate |
Babel updating the cxx implementation of component demo.Driver ...
This last bocca create decorates our Driver component with a CCA standard go port, which is not specified as part of this project. Since gov.cca.ports.GoPort is a part of the CCA specification, Bocca takes care of knowing where to find the SIDL definition of this port. The special --go option allows Bocca to generate a default go implementation which prefetches the uses ports so that all the user needs to do for our example is add numerical code. In languages which are not object-oriented, this substantially reduces the errors in handling ports, exceptions, and memory deallocation.
| Note It is not necessary to know at component creation time all ports that will be used or provided or other implementation details. Bocca provides various commands for changing project entities, e.g. adding or removing uses and provides ports. |
As we have defined a number of new things, this would be a good time to rebuild the project:
| $ | make |
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # Building in ports/, languages: cxx # ======================================================================= ## Building ports... [c] using Babel to generate cxx client code for demo.FunctionPort... [c] creating library: libdemo.FunctionPort-cxx.la... [c] installing demo.FunctionPort.sidl [c] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.FunctionPort_depl.xml ... 11071 [c] using Babel to generate cxx client code for demo.Integration... [c] creating library: libdemo.Integration-cxx.la... [c] installing demo.Integration.sidl [c] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Integration_depl.xml ... 11404 # ======================================================================= # Building in components/clients/, languages: cxx # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: cxx # ======================================================================= [s] Building class/component demo.Driver: [s] using Babel to generate cxx implementation code from demo.Driver.sidl... [s] compiling sources... [s] creating class/component library: libdemo.Driver.la ... [s] finished libtooling: components/demo.Driver/libdemo.Driver.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Driver_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Function: [s] using Babel to generate cxx implementation code from demo.Function.sidl... [s] compiling sources... [s] creating class/component library: libdemo.Function.la ... [s] finished libtooling: components/demo.Function/libdemo.Function.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Function_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Integrator: [s] using Babel to generate cxx implementation code from demo.Integrator.sidl... [s] compiling sources... [s] creating class/component library: libdemo.Integrator.la ... [s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Integrator_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.emptyComponent: doing nothing -- library is up-to-date. Build summary: SUCCESS building demo.Driver SUCCESS building demo.Function SUCCESS building demo.Integrator ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
Note that this operation can be very time-consuming when your project is managing many ports and components with the fully supported set of Babel language bindings.
Running make check will test whether the components you've created can be instantiated successfully in the Ccaffeine framework:
| $ | make check |
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo' make --no-print-directory --no-builtin-rules -C components check ### Test library load and instantiation for the following languages: cxx Running instantiation tests only Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log SUCCESS: ==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log). make --no-print-directory --no-builtin-rules check-user make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
So far, with very little work, we have generated what appears to be an application but is really just the componentized shell of an application. If you were to run the GUI (Section 2.1) or do the command-line equivalent in Ccaffeine (Section 2.2), you would find that the components are decorated with the ports you expect, and they can even be connected (an operation of the framework, not of the components or ports). But if you attempted to run an application using these components, nothing would happen because they're only skeletons, providing the component-ness, but not the functionality.
| Note Along with everyting else it does, Bocca generates a set of utility scripts which are tailored to the specific Bocca project (i.e. having the right paths, the right sets of components, etc.). In Section 2 you ran Bocca-generated utility scripts in the pre-built tutorial tree (i.e. $TUTORIAL_SRC/utils/run-gui.sh). When you're working in your own Bocca projects, make sure you use the utility scripts associated with that project, or things won't work properly. |
The next step in developing components requires you implement the component's intended functionality within the Bocca -generated skeleton. There are two places that we have to change things to make that happen: add methods to the interface definitions (.sidl file) and then put the implementation code into the components in the language chosen in Section 3.1.
Because Bocca generates all the files in the project, it knows where to find the code associated with each SIDL symbol. Using the bocca edit command, you can specify the SIDL symbol you're interested in and Bocca will bring up the appropriate file in your editor of choice. Additionally, after you exit the editor, Bocca regenerates all other source files that depend on the source file edited.
| $ | bocca edit SIDL_SYMBOL |
| $ | bocca edit -m SIDL_CLASS |
| $ | bocca edit -i SIDL_CLASS |
| $ | bocca edit -i SIDL_CLASS METHOD_NAME |
The last invocation requires that your editor support the +N option, which is used for specifying the initial position in the file. All emacs and vi versions support this feature. If your favorite editor does not support +N, omit the method name and search for it in the opened file using the editor's search capability.
If you replace edit in any of the above, with whereis, Bocca prints out the path of the file that would be edited without starting up an editor.
The environment variable BOCCA_EDITOR (and if that is not set, then EDITOR) controls what editor gets invoked by bocca edit.
| Tip If you need to set BOCCA_EDITOR to get the editor you want, you might want to add the appropriate setting to your login files. |
| Tip Users of emacs may want to set BOCCA_EDITOR to ``emacs -nw'' when editing on a remote cluster with slow or no X11 connections. |
There is also a way for you to tell Bocca that you've edited a file by some means other than bocca edit.
| $ | bocca edit --touch FunctionPort |
| $ | bocca edit --touch -i Driver |
If you do not tell Bocca about files you've modified, you might find that the project's files are not in a consistent state. For example, if you add a method to a .sidl file it will not appear it in the implementation file until Bocca updates it.
In Section 3.2.1, we had Bocca create skeleton .sidl files for the Integration and FunctionPort . Now we need to flesh out the ports by actually specifying the methods they contain.
| $ | bocca edit Integration |
The SIDL code for Integration looks like this:
// DO-NOT-DELETE bocca.splicer.begin(demo.comment)
// Insert-UserCode-Here {demo.comment} (Insert your package comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.comment)
package demo version 0.0 {
// DO-NOT-DELETE bocca.splicer.begin(demo.Integration.comment)
// Insert-UserCode-Here {demo.Integration.comment} (Insert your port comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.Integration.comment)
interface Integration extends gov.cca.Port
{
// DO-NOT-DELETE bocca.splicer.begin(demo.Integration.methods)
// Insert-UserCode-Here {demo.Integration.methods} (Insert your port methods here)
// DO-NOT-DELETE bocca.splicer.end(demo.Integration.methods)
}
}
Insert the march method:
// DO-NOT-DELETE bocca.splicer.begin(demo.comment)
// Insert-UserCode-Here {demo.comment} (Insert your package comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.comment)
package demo version 0.0 {
// DO-NOT-DELETE bocca.splicer.begin(demo.Integration.comment)
// Insert-UserCode-Here {demo.Integration.comment} (Insert your port comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.Integration.comment)
interface Integration extends gov.cca.Port
{
// DO-NOT-DELETE bocca.splicer.begin(demo.Integration.methods)
double march(in double lowBound, in double upBound, in int count);
// DO-NOT-DELETE bocca.splicer.end(demo.Integration.methods)
}
}
After you quit the editor, bocca edit then automatically updates the components that depend on the port edited:
Updating makefiles (for demo.Integration, demo.Integrator, demo.Driver)... Using Babel to validate the SIDL for port demo.Integration ... Babel updating the cxx implementation of component demo.Integrator ... Babel updating the cxx implementation of component demo.Driver ...
Next edit the file FunctionPort.sidl:
$
bocca edit FunctionPort
Add two methods, init and evaluate so that function looks like this:
// DO-NOT-DELETE bocca.splicer.begin(demo.comment)
// Insert-UserCode-Here {demo.comment} (Insert your package comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.comment)
package demo version 0.0 {
// DO-NOT-DELETE bocca.splicer.begin(demo.FunctionPort.comment)
// Insert-UserCode-Here {demo.FunctionPort.comment} (Insert your port comments here)
// DO-NOT-DELETE bocca.splicer.end(demo.FunctionPort.comment)
interface FunctionPort extends gov.cca.Port
{
// DO-NOT-DELETE bocca.splicer.begin(demo.FunctionPort.methods)
void init(in array<double,1> params);
double evaluate(in double x);
// DO-NOT-DELETE bocca.splicer.end(demo.FunctionPort.methods)
}
}
Again quit the editor and the dependent components are updated as indicated by this output from bocca edit:
Updating makefiles (for demo.FunctionPort, demo.Function, demo.Integrator)... Using Babel to validate the SIDL for port demo.FunctionPort ... Babel updating the cxx implementation of component demo.Function ... Babel updating the cxx implementation of component demo.Integrator ...
These files contain the language-independent specification of the ports and their methods, expressed using SIDL. When you type make all of the the new method information is propagated to the language-dependent implementation files using Babel . Of course the methods will be unimplemented but the components will build anyway. So let's do that now:
$
make && make check
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
# =======================================================================
# No SIDL files in external/sidl, skipping build for external
# =======================================================================
# =======================================================================
# Building in ports/, languages: cxx
# =======================================================================
## Building ports...
[c] using Babel to generate cxx client code for demo.FunctionPort...
[c] creating library: libdemo.FunctionPort-cxx.la...
15,16c15,16
<
< // Insert-UserCode-Here {demo.FunctionPort.methods} (Insert your port methods here)
---
> void init(in array<double,1> params);
> double evaluate(in double x);
[c] installing modified demo.FunctionPort.sidl in /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca.
[c] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.FunctionPort_depl.xml ... 24528
[c] using Babel to generate cxx client code for demo.Integration...
[c] creating library: libdemo.Integration-cxx.la...
15,16c15
<
< // Insert-UserCode-Here {demo.Integration.methods} (Insert your port methods here)
---
> double march(in double lowBound, in double upBound, in int count);
[c] installing modified demo.Integration.sidl in /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca.
[c] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Integration_depl.xml ... 24834
# =======================================================================
# Building in components/clients/, languages: cxx
# =======================================================================
## Building clients...
# =======================================================================
# Building in components/, languages: cxx
# =======================================================================
[s] Building class/component demo.Driver:
[s] using Babel to generate cxx implementation code from demo.Driver.sidl...
[s] compiling sources...
[s] creating class/component library: libdemo.Driver.la ...
[s] finished libtooling: components/demo.Driver/libdemo.Driver.la ...
[s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Driver_depl.xml ...
[s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)...
[s] Building class/component demo.Function:
[s] using Babel to generate cxx implementation code from demo.Function.sidl...
[s] compiling sources...
[s] creating class/component library: libdemo.Function.la ...
[s] finished libtooling: components/demo.Function/libdemo.Function.la ...
[s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Function_depl.xml ...
[s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)...
[s] Building class/component demo.Integrator:
[s] using Babel to generate cxx implementation code from demo.Integrator.sidl...
[s] compiling sources...
[s] creating class/component library: libdemo.Integrator.la ...
[s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ...
[s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Integrator_depl.xml ...
[s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)...
[s] Building class/component demo.emptyComponent:
doing nothing -- library is up-to-date.
Build summary:
SUCCESS building demo.Driver
SUCCESS building demo.Function
SUCCESS building demo.Integrator
### To test instantiation of successfully built components, run 'make check' ###
################ Finished building everything #################
####### You can run some simple tests with 'make check' #######
make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
make --no-print-directory --no-builtin-rules -C components check
### Test library load and instantiation for the following languages: cxx
Running instantiation tests only
Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc
Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log
SUCCESS:
==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log).
make --no-print-directory --no-builtin-rules check-user
make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
The methods you inserted into the SIDL port specifications have now been inserted into your already generated components. At this point we are ready to insert the actual implementation into the bodies of these methods. Notice that up to this point, you created the skeleton for an entire application without having to write any code at all, except for the SIDL for the ports. Finally, it is time to implement the components' functionality in the programming language of your choice.
Please jump to the appropriate section for the implementation language
you chose in Section 3.1 and then continue with
Section 3.6 (p.
).
![]()
Note
Assumes you created the project with bocca create
project demo --language=cxx or did not specify a language
(cxx is the default).
Edit the evaluate method in the implementation file (also known as ``the impl'') that Bocca has generated for you (by invoking Babel ). Use the bocca edit -i to go directly to each method.
$
bocca edit -i Function evaluate
The editor opens up in the place where the implementation code for evaluate must be put. You see a default implementation generated by Babel for all user methods: the throwing of an exception which says the method is not yet implemented.
double
demo::Function_impl::evaluate_impl (
/* in */double x )
{
// DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
// Insert-Code-Here {demo.Function.evaluate} (evaluate method)
//
// This method has not been implemented
//
// DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Function.evaluate)
::sidl::NotImplementedException ex = ::sidl::NotImplementedException::_create();
ex.setNote("This method has not been implemented");
ex.add(__FILE__, __LINE__, "evaluate");
throw ex;
// DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Function.evaluate)
// DO-NOT-DELETE splicer.end(demo.Function.evaluate)
}
As the comment suggests, this method is ``not implemented'', but some
code has been inserted by Babel to make sure an exception is thrown
to inform the user if this method is called by mistake. Delete this
exception code and substitute an implementation for the
PiFunction (i.e., the integral from 0 to 1 of
4/(1 + x2)
is an approximation of
).
// DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
return 4.0 / (1.0 + x * x);
// DO-NOT-DELETE splicer.end(demo.Function.evaluate)
Now in the same file just above the evaluate method, find the second method for the FunctionPort init method:
// DO-NOT-DELETE splicer.begin(demo.Function.init)
// Do nothing.
// DO-NOT-DELETE splicer.end(demo.Function.init)
We don't have any initialization in this simple example, so we just eliminate the code that throws the exception when the method is executed.
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/demo.Function/demo_Function_Impl.cxx
Similarly edit the march method in the Integrator with
$
bocca edit -i Integrator march
double
demo::Integrator_impl::march_impl (
/* in */double lowBound,
/* in */double upBound,
/* in */int32_t count )
{
// DO-NOT-DELETE splicer.begin(demo.Integrator.march)
// Insert-Code-Here {demo.Integrator.march} (march method)
//
// This method has not been implemented
//
// DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Integrator.march)
::sidl::NotImplementedException ex = ::sidl::NotImplementedException::_create();
ex.setNote("This method has not been implemented");
ex.add(__FILE__, __LINE__, "march");
throw ex;
// DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Integrator.march)
// DO-NOT-DELETE splicer.end(demo.Integrator.march)
}
Again remove this boilerplate exception code and insert an implementation of the Trapezoid rule for integration that uses the FunctionPort :
// DO-NOT-DELETE splicer.begin(demo.Integrator.march)
demo::FunctionPort odeRHS;
gov::cca::Port generalPort;
try {
generalPort = d_services.getPort("odeRHS");
} catch ( ::gov::cca::CCAException ex) {
// we cannot go on. add to the error report.
ex.add( __FILE__, __LINE__,
"odeRHS port not available in Integrator.march");
throw;
}
odeRHS = ::babel_cast< demo::FunctionPort >(generalPort);
if (odeRHS._is_nil()){
// we cannot go on. toss an exception after cleaning up.
try {
d_services.releasePort("odeRHS");
} catch (...) {
// suppress framework complaints; we're already handling an exception.
}
::sidl::SIDLException ex = ::sidl::SIDLException::_create();
ex.setNote("Error: odeRHS port is nil. Weird.");
ex.add(__FILE__, __LINE__, "demo::Integrator::integrate_impl");
throw ex;
}
double h = (upBound - lowBound) / count;
double retval = 0.0;
double sum = 0.0;
for (int i = 1; i <= count; i++){
sum += odeRHS.evaluate(lowBound + (i - 1) * h) +
odeRHS.evaluate(lowBound + i * h);
}
retval = h/2.0 * sum;
d_services.releasePort("odeRHS");
return retval;
// DO-NOT-DELETE splicer.end(demo.Integrator.march)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/demo.Integrator/demo_Integrator_Impl.cxx
Finally for the Driver component we have to implement the GoPort details to get things going. Bocca will take you to the generated method, which looks like this:
$
bocca edit -i Driver go
int32_t
demo::Driver_impl::go_impl ()
{
// DO-NOT-DELETE splicer.begin(demo.Driver.go)
// User editable portion is in the middle at the next Insert-UserCode-Here line.
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog)
int bocca_status = 0;
// The user's code should set bocca_status 0 if computation proceeded ok.
// The user's code should set bocca_status -1 if computation failed but might
// succeed on another call to go(), e.g. when a required port is not yet
// connected.
// The user's code should set bocca_status -2 if the computation failed and
// can never succeed in a future call.
// The user's code should NOT use return in this function.
// Exceptions that are not caught in user code will be converted to
// status -2.
gov::cca::Port port;
// nil if not fetched and cast successfully:
demo::Integration integrate;
// True when releasePort is needed (even if cast fails):
bool integrate_fetched = false;
// Use a demo.Integration port with port name integrate
try{
port = this->d_services.getPort("integrate");
} catch ( ::gov::cca::CCAException ex ) {
// we will continue with port nil (never successfully assigned) and
// set a flag.
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error calling getPort(\"integrate\") "
" at " << __FILE__ << ":" << __LINE__ -5 << ": " << ex.getNote()
<< std::endl;
#endif // _BOCCA_STDERR
}
if ( port._not_nil() ) {
// even if the next cast fails, must release.
integrate_fetched = true;
integrate = ::babel_cast< demo::Integration >(port);
if (integrate._is_nil()) {
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error casting gov::cca::Port "
<< "integrate to type "
<< "demo::Integration" << std::endl;
#endif //_BOCCA_STDERR
goto BOCCAEXIT; // we cannot correctly continue. clean up and leave.
}
}
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog)
// When this try/catch block is rewritten by the user, we will not change it.
try {
// All port instances should be rechecked for ._not_nil before calling in
// user code. Not all ports need be connected in arbitrary use.
// The uses ports appear as local variables here named exactly as on the
// bocca commandline.
// Insert-UserCode-Here {demo.Driver.go}
// REMOVE ME BLOCK.begin(demo.Driver.go)
#ifdef _BOCCA_STDERR
std::cerr << "USER FORGOT TO FILL IN THEIR GO FUNCTION HERE." << std::endl;
#endif
// REMOVE ME BLOCK.end(demo.Driver.go)
}
// If unknown exceptions in the user code are tolerable and restart is ok,
// return -1 instead. -2 means the component is so confused that it and
// probably the application should be destroyed.
// babel requires exact exception catching due to c++ binding of interfaces.
catch (gov::cca::CCAException ex) {
bocca_status = -2;
std::string enote = ex.getNote();
#ifdef _BOCCA_STDERR
std::cerr << "CCAException in user go code: " << enote << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;;
#endif
}
catch (sidl::RuntimeException ex) {
bocca_status = -2;
std::string enote = ex.getNote();
#ifdef _BOCCA_STDERR
std::cerr << "RuntimeException in user go code: " << enote << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;;
#endif
}
catch (sidl::SIDLException ex) {
bocca_status = -2;
std::string enote = ex.getNote();
#ifdef _BOCCA_STDERR
std::cerr << "SIDLException in user go code: " << enote << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;;
#endif
}
catch (sidl::BaseException ex) {
bocca_status = -2;
std::string enote = ex.getNote();
#ifdef _BOCCA_STDERR
std::cerr << "BaseException in user go code: " << enote << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;;
#endif
}
catch (std::exception ex) {
bocca_status = -2;
#ifdef _BOCCA_STDERR
std::cerr << "C++ exception in user go code: " << ex.what() << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;
#endif
}
catch (...) {
bocca_status = -2;
#ifdef _BOCCA_STDERR
std::cerr << "Odd exception in user go code " << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;
#endif
}
BOCCAEXIT:; // target point for error and regular cleanup. do not delete.
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog)
// release integrate
if (integrate_fetched) {
integrate_fetched = false;
try{
this->d_services.releasePort("integrate");
} catch ( ::gov::cca::CCAException ex ) {
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error calling releasePort("
<< "\"integrate\") at "
<< __FILE__ << ":" << __LINE__ -4 << ": " << ex.getNote()
<< std::endl;
#endif // _BOCCA_STDERR
}
// c++ port reference will be dropped when function exits, but we
// must tell framework.
}
return bocca_status;
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog)
//
// This method has not been implemented
//
// DO-NOT-DELETE splicer.end(demo.Driver.go)
}
Find the REMOVE block within the go method implementation, delete it, and insert the numerical logic needed to use the integrator.IntegratorPort port. Any required local variables should be inserted just before the boccaGoProlog protected block.
The go subroutine will be called by the framework when the component's run button (the name of this particular GoPort instance) is pushed in the GUI. Bocca generates the code to the Integration that the Driver is connected to. We just have to use it to compute the integral and return the proper value for bocca_status.
// DO-NOT-DELETE splicer.begin(demo.Driver.go)
// User editable portion is in the middle at the next Insert-UserCode-Here line.
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog)
int bocca_status = 0;
// The user's code should set bocca_status 0 if computation proceeded ok.
// The user's code should set bocca_status -1 if computation failed but might
// succeed on another call to go(), e.g. when a required port is not yet
// connected.
// The user's code should set bocca_status -2 if the computation failed and
// can never succeed in a future call.
// The user's code should NOT use return in this function.
// Exceptions that are not caught in user code will be converted to
// status -2.
gov::cca::Port port;
// nil if not fetched and cast successfully:
demo::Integration integrate;
// True when releasePort is needed (even if cast fails):
bool integrate_fetched = false;
// Use a demo.Integration port with port name integrate
try{
port = this->d_services.getPort("integrate");
} catch ( ::gov::cca::CCAException ex ) {
// we will continue with port nil (never successfully assigned) and
// set a flag.
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error calling getPort(\"integrate\") "
" at " << __FILE__ << ":" << __LINE__ -5 << ": " << ex.getNote()
<< std::endl;
#endif // _BOCCA_STDERR
}
if ( port._not_nil() ) {
// even if the next cast fails, must release.
integrate_fetched = true;
integrate = ::babel_cast< demo::Integration >(port);
if (integrate._is_nil()) {
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error casting gov::cca::Port "
<< "integrate to type "
<< "demo::Integration" << std::endl;
#endif //_BOCCA_STDERR
goto BOCCAEXIT; // we cannot correctly continue. clean up and leave.
}
}
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog)
// When this try/catch block is rewritten by the user, we will not change it.
try {
// All port instances should be rechecked for ._not_nil before calling in
// user code. Not all ports need be connected in arbitrary use.
// The uses ports appear as local variables here named exactly as on the
// bocca commandline.
// Insert-UserCode-Here {demo.Driver.go}
double value;
int count = 100000;
double lowerBound = 0.0, upperBound = 1.0;
// operate on the port
value = integrate.march(lowerBound, upperBound, count);
std::cout << "Value = " << value << std::endl;
}
// If unknown exceptions in the user code are tolerable and restart is ok,
// return -1 instead. -2 means the component is so confused that it and
// probably the application should be destroyed.
catch (sidl::BaseException ex) {
bocca_status = -2;
std::string enote = ex.getNote();
#ifdef _BOCCA_STDERR
std::cerr << "Exception in user go code: " << enote << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;;
#endif
}
catch (std::exception ex) {
bocca_status = -2;
#ifdef _BOCCA_STDERR
std::cerr << "C++ exception in user go code: " << ex.what() << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;
#endif
}
catch (...) {
bocca_status = -2;
#ifdef _BOCCA_STDERR
std::cerr << "Odd exception in user go code " << std::endl;
std::cerr << "Returning -2 from go()" << std::endl;
#endif
}
BOCCAEXIT:; // target point for error and regular cleanup. do not delete.
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog)
// release integrate
if (integrate_fetched) {
integrate_fetched = false;
try{
this->d_services.releasePort("integrate");
} catch ( ::gov::cca::CCAException ex ) {
#ifdef _BOCCA_STDERR
std::cerr << "demo.Driver: Error calling releasePort("
<< "\"integrate\") at "
<< __FILE__ << ":" << __LINE__ -4 << ": " << ex.getNote()
<< std::endl;
#endif // _BOCCA_STDERR
}
// c++ port reference will be dropped when function exits, but we
// must tell framework.
}
return bocca_status;
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog)
// DO-NOT-DELETE splicer.end(demo.Driver.go)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited.
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/demo.Driver/demo_Driver_Impl.cxx
Now remake your project tree to finish the components:
$
make
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # Building in ports/, languages: cxx # ======================================================================= ## Building ports... # ======================================================================= # Building in components/clients/, languages: cxx # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: cxx # ======================================================================= [s] Building class/component demo.Driver: [s] creating class/component library: libdemo.Driver.la ... [s] finished libtooling: components/demo.Driver/libdemo.Driver.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Driver_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Function: [s] creating class/component library: libdemo.Function.la ... [s] finished libtooling: components/demo.Function/libdemo.Function.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Function_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Integrator: [s] creating class/component library: libdemo.Integrator.la ... [s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/install/share/cca/demo/demo.Integrator_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.emptyComponent: doing nothing -- library is up-to-date. Build summary: SUCCESS building demo.Driver SUCCESS building demo.Function SUCCESS building demo.Integrator ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
It is good practice to do a make check at this point:
$
make check
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo' make --no-print-directory --no-builtin-rules -C components check ### Test library load and instantiation for the following languages: cxx Running instantiation tests only Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log SUCCESS: ==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo/components/tests/instantiation.gen.rc.log). make --no-print-directory --no-builtin-rules check-user make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/cxx/demo'
You should now be able to instantiate these components, assemble them into an application, and run the application, following the same procedures as in Section 2, and get a result that's reasonably close to pi .
![]()
Note
Assumes you created the project with bocca create
project demo --language=f90.
Edit the evaluate method in the implementation file (also known as ``the impl'') that Bocca has generated for you (by invoking Babel ). Use the bocca edit -i to go directly to each method.
$
bocca edit -i Function evaluate
The editor opens up in the place where the implementation code for evaluate must be put. You see a default implementation generated by Babel for all user methods: the throwing of an exception which says the method is not yet implemented.
recursive subroutine demo_Function_evaluate_mi(self, x, retval, exception)
use sidl
use sidl_NotImplementedException
use sidl_BaseInterface
use sidl_RuntimeException
use demo_Function
use demo_Function_impl
! DO-NOT-DELETE splicer.begin(demo.Function.evaluate.use)
! Insert-Code-Here {demo.Function.evaluate.use} (use statements)
! DO-NOT-DELETE splicer.end(demo.Function.evaluate.use)
implicit none
type(demo_Function_t) :: self
! in
real (kind=sidl_double) :: x
! in
real (kind=sidl_double) :: retval
! out
type(sidl_BaseInterface_t) :: exception
! out
! DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
! Insert-Code-Here {demo.Function.evaluate} (evaluate method)
!
! This method has not been implemented
!
! DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Function.evaluate)
type(sidl_BaseInterface_t) :: throwaway
type(sidl_NotImplementedException_t) :: notImpl
call new(notImpl, exception)
call setNote(notImpl, 'Not Implemented', exception)
call cast(notImpl, exception,throwaway)
call deleteRef(notImpl,throwaway)
return
! DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Function.evaluate)
! DO-NOT-DELETE splicer.end(demo.Function.evaluate)
end subroutine demo_Function_evaluate_mi
As the comment suggests, this method is ``not implemented'', but some
code has been inserted by Babel to make sure an exception is thrown
to inform the user if this method is called by mistake. Delete this
exception code and substitute an implementation for the
PiFunction (i.e., the integral from 0 to 1 of
4/(1 + x2)
is an approximation of
).
! DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
retval = 4.0 / (1.0 + x * x)
! DO-NOT-DELETE splicer.end(demo.Function.evaluate)
Now in the same file just above the evaluate method, find the second method for the FunctionPort init method:
! DO-NOT-DELETE splicer.begin(demo.Function.init)
! Do nothing
! DO-NOT-DELETE splicer.end(demo.Function.init)
We don't have any initialization in this simple example, so we just eliminate the code that throws the exception when the method is executed.
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/demo.Function/demo_Function_Impl.F90
Similarly edit the march method in the Integrator with
$
bocca edit -i Integrator march
recursive subroutine demo_Integrator_march_mi(self, lowBound, upBound, count, &
retval, exception)
use sidl
use sidl_NotImplementedException
use sidl_BaseInterface
use sidl_RuntimeException
use demo_Integrator
use demo_Integrator_impl
! DO-NOT-DELETE splicer.begin(demo.Integrator.march.use)
! Insert-Code-Here {demo.Integrator.march.use} (use statements)
! DO-NOT-DELETE splicer.end(demo.Integrator.march.use)
implicit none
type(demo_Integrator_t) :: self
! in
real (kind=sidl_double) :: lowBound
! in
real (kind=sidl_double) :: upBound
! in
integer (kind=sidl_int) :: count
! in
real (kind=sidl_double) :: retval
! out
type(sidl_BaseInterface_t) :: exception
! out
! DO-NOT-DELETE splicer.begin(demo.Integrator.march)
! Insert-Code-Here {demo.Integrator.march} (march method)
!
! This method has not been implemented
!
! DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Integrator.march)
type(sidl_BaseInterface_t) :: throwaway
type(sidl_NotImplementedException_t) :: notImpl
call new(notImpl, exception)
call setNote(notImpl, 'Not Implemented', exception)
call cast(notImpl, exception,throwaway)
call deleteRef(notImpl,throwaway)
return
! DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Integrator.march)
! DO-NOT-DELETE splicer.end(demo.Integrator.march)
end subroutine demo_Integrator_march_mi
Again remove this boilerplate exception code and insert an implementation of the Trapezoid rule for integration that uses the FunctionPort :
! DO-NOT-DELETE splicer.begin(demo.Integrator.march.use)
! the port types we need go here. use gov_cca_Port use demo_FunctionPort
! DO-NOT-DELETE splicer.end(demo.Integrator.march.use)
! DO-NOT-DELETE splicer.begin(demo.Integrator.march)
! User's local declarations. We follow the pattern generated for us in Driver.go()
type(gov_cca_Port_t) :: port
type(gov_cca_Services_t) :: services
type(SIDL_BaseInterface_t) :: throwaway
type(SIDL_BaseInterface_t) :: dumex
type(demo_Integrator_wrap) :: dp
logical dr_port ! if dr_X true, the deleteRef(X) is needed before return.
type(demo_FunctionPort_t) :: odeRHS__p
! odeRHS__p is non-null if specific uses port obtained.
logical odeRHS_fetched
! odeRHS_fetched true if releaseport is needed for this port.
! a small message catalog for exception reporting
character (LEN=*) errMsg00
character (LEN=*) errMsg0
character (LEN=*) errMsg1
character (LEN=*) errMsg2
character (LEN=*) errMsg3
character (LEN=*) errMsg4
parameter(errMsg00= &
'NULL d_services pointer in demo.Integrator.march()')
parameter(errMsg0= &
'demo.Integrator: Error go() getPort(odeRHS) failed.')
parameter(errMsg1= &
'demo.Integrator: Error casting odeRHS to FunctionPort')
parameter(errMsg2= &
'demo.Integrator: Error in deleteRef(port) while getting odeRHS')
parameter(errMsg3= &
'demo.Integrator: Error calling releasePort(odeRHS).')
parameter(errMsg4= &
'demo.Integrator: Error in deleteRef for port odeRHS.')
! numerical method variable, other than the call arguments:
real (kind=sidl_double) :: h, fvalueleft, fvalueright, sum, left, right
integer i
BOCCA_EXTERNAL
! not crashing if something fails .eq. good bookkeeping and exception handling.
! start with initialization
call set_null( odeRHS__p)
odeRHS_fetched = .false.
call set_null(services)
call set_null(port)
call set_null(throwaway)
call set_null(dumex)
dr_port = .false.
call demo_Integrator__get_data_m(self,dp);
services = dp%d_private_data%d_services
retval = -4.0
if (is_null(services) ) then
call BOCCA_SIDL_THROW_F90(exception, errMsg00)
endif
! Use a demo.FunctionPort port with port name odeRHS
call getPort(services,"odeRHS", port, exception)
BOCCA_SIDL_CHECK_F90(exception, errMsg0)
odeRHS_fetched = .true.
! even if the next cast fails, must releasePort per odeRHS_fetched.
call cast(port, odeRHS__p, exception)
BOCCA_SIDL_CHECK_F90(exception, errMsg1)
! done with the generic port pointer. drop it.
call deleteRef(port, exception)
call set_null(port)
BOCCA_SIDL_CHECK_F90(exception, errMsg2)
!! here's the numerical work
! the trapezoidal rule
h = (upBound - lowBound) / count
sum = 0.0
fvalueleft = 0.0
fvalueright = 0.0
do i = 1,count
left = lowBound + (i - 1) * h
call evaluate(odeRHS__p, left, fvalueleft, exception)
BOCCA_SIDL_CHECK_F90(exception, 'error calculating fvalueleft')
right = lowBound + i * h
call evaluate(odeRHS__p, right, fvalueright, exception)
BOCCA_SIDL_CHECK_F90(exception, 'error calculating fvalueright')
sum = sum + fvalueleft + fvalueright
enddo
retval = h/2.0 * sum;
!! the numerical work is done.
BOCCAEXIT continue ! target point for normal and error cleanup.
if (not_null(port)) then
call deleteRef(port,throwaway)
call checkException(self, throwaway, 'cleanup port error', .false., dumex)
call set_null(port)
endif
! release odeRHS
if (odeRHS_fetched) then
odeRHS_fetched = .false.
call releasePort(services, 'odeRHS', throwaway)
call checkException(self, throwaway, errMsg3, .false., dumex)
if ( not_null(odeRHS__p) ) then
call deleteRef(odeRHS__p, throwaway)
call checkException(self, throwaway, errMsg4, .false., dumex)
call set_null(odeRHS__p)
endif
endif
! DO-NOT-DELETE splicer.end(demo.Integrator.march)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/demo.Integrator/demo_Integrator_Impl.F90
Finally for the Driver component we have to implement the GoPort details to get things going. Bocca will take you to the generated method, which looks like this:
$
bocca edit -i Driver go
recursive subroutine demo_Driver_go_mi(self, retval, exception)
use sidl
use sidl_NotImplementedException
use sidl_BaseInterface
use sidl_RuntimeException
use demo_Driver
use demo_Driver_impl
! DO-NOT-DELETE splicer.begin(demo.Driver.go.use)
! Bocca generated code. bocca.protected.begin(demo.Driver.go.use)
use gov_cca_Port
use demo_Integration
! Bocca generated code. bocca.protected.end(demo.Driver.go.use)
! DO-NOT-DELETE splicer.end(demo.Driver.go.use)
implicit none
type(demo_Driver_t) :: self
! in
integer (kind=sidl_int) :: retval
! out
type(sidl_BaseInterface_t) :: exception
! out
! DO-NOT-DELETE splicer.begin(demo.Driver.go)
! Insert-User-Declarations-Here
! Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog)
integer bocca_status
! The user's code should set bocca_status 0 if computation proceeded ok.
! The user's code should set bocca_status -1 if computation failed but might
! succeed on another call to go(), e.g. wheh a required port is not yet connected.
! The user's code should set bocca_status -2 if the computation failed and can
! never succeed in a future call.
! The users's code should NOT use return in this function;
! Exceptions that are not caught in user code will be converted to status -2.
!
type(gov_cca_Port_t) :: port
type(gov_cca_Services_t) :: services
type(SIDL_BaseInterface_t) :: throwaway
type(SIDL_BaseInterface_t) :: dumex
type(demo_Driver_wrap) :: dp
logical dr_port ! if dr_X true, the deleteRef(X) is needed before return.
type(demo_Integration_t) :: integrate__p ! non-null if specific uses port obtained.
logical integrate_fetched ! true if releaseport is needed for this port.
character (LEN=*) errMsg0_integrate
character (LEN=*) errMsg1_integrate
character (LEN=*) errMsg2_integrate
character (LEN=*) errMsg3_integrate
character (LEN=*) errMsg4_integrate
parameter(errMsg0_integrate= &
'demo.Driver: Error go() getPort(integrate) failed.')
parameter(errMsg1_integrate= &
'demo.Driver: Error casting gov.cca.Port integrate to type demo.Integration')
parameter(errMsg2_integrate= &
'demo.Driver: Error in deleteRef(port) while getting integrate')
parameter(errMsg3_integrate= &
'demo.Driver: Error calling releasePort(integrate). Continuing.')
parameter(errMsg4_integrate = &
'demo.Driver: Error in deleteRef for port integrate. Continuing.')
BOCCA_EXTERNAL
! not crashing if something fails requires good bookkeeping and exception handling.
call set_null(services)
call set_null(port)
call set_null(throwaway)
call set_null(dumex)
dr_port = .false.
bocca_status = 0
call demo_Driver__get_data_m(self,dp);
services = dp%d_private_data%d_services
if (is_null(services) ) then
call BOCCA_SIDL_THROW_F90(exception, 'NULL d_services pointer in demo.Driver.go()')
endif
! Use a demo.Integration port with port name integrate
call getPort(services,"integrate", port, throwaway)
if ( not_null(throwaway) ) then
call set_null(port)
call checkException(self, throwaway, errMsg0_integrate, .false., dumex)
! we will continue with port null (never successfully assigned) and set a flag.
endif
call set_null( integrate__p)
integrate_fetched = .false.
if ( not_null(port)) then
integrate_fetched = .true. ! even if the next cast fails, must releasePort.
call cast(port, integrate__p, exception)
BOCCA_SIDL_CHECK_F90(exception, errMsg1_integrate)
call deleteRef(port, exception)
call set_null(port)
BOCCA_SIDL_CHECK_F90(exception, errMsg2_integrate)
endif
! Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog)
! When this block is rewritten by the user, we will not change it.
! All port instances should be rechecked for NULL before calling in user code.
! Not all ports need be connected in arbitrary use.
! The port instance names used in registerUsesPort appear as local variable
! names here with the suffix __p added.
! BEGIN REMOVE ME BLOCK
#ifdef _BOCCA_STDERR
write(*,*) 'USER FORGOT TO FILL IN THEIR FUNCTION demo.Driver.go.'
#endif
! END REMOVE ME BLOCK
! If unknown exceptions in the user code are tolerable and restart is ok,
! set bocca_status -1 instead.
! -2 means the component is so confused that it and probably the application
! should be destroyed.
!
BOCCAEXIT continue ! target point for normal and error cleanup. do not delete.
! Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog)
if (not_null(port)) then
call deleteRef(port,throwaway)
call checkException(self, throwaway, 'cleanup port error', .false., dumex)
call set_null(port)
endif
! release integrate
if (integrate_fetched) then
integrate_fetched = .false.
call releasePort(services, 'integrate', throwaway)
call checkException(self, throwaway, errMsg3_integrate, .false., dumex)
if ( not_null(integrate__p) ) then
call deleteRef(integrate__p, throwaway)
call checkException(self, throwaway, errMsg4_integrate, .false., dumex)
call set_null(integrate__p)
endif
endif
! Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog)
! Insert-User-Exception-Cleanup-Here
retval = bocca_status
!
! This method has not been implemented
!
! DO-NOT-DELETE splicer.end(demo.Driver.go)
end subroutine demo_Driver_go_mi
Find the REMOVE block within the go method implementation, delete it, and insert the numerical logic needed to use the integrator.IntegratorPort port. Any required local variables should be inserted just before the boccaGoProlog protected block.
The go subroutine will be called by the framework when the component's run button (the name of this particular GoPort instance) is pushed in the GUI. Bocca generates the code to the Integration that the Driver is connected to. We just have to use it to compute the integral and return the proper value for bocca_status.
! DO-NOT-DELETE splicer.begin(demo.Driver.go) ! Insert-User-Declarations-Here
! local variables for integration real (kind=sidl_double) :: lowBound real (kind=sidl_double) :: upBound integer (kind=sidl_int) :: count real (kind=sidl_double) :: value
! Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog)
integer bocca_status
! The user's code should set bocca_status 0 if computation proceeded ok.
! The user's code should set bocca_status -1 if computation failed but might
! succeed on another call to go(), e.g. wheh a required port is not yet connected.
! The user's code should set bocca_status -2 if the computation failed and can
! never succeed in a future call.
! The users's code should NOT use return in this function;
! Exceptions that are not caught in user code will be converted to status -2.
!
type(gov_cca_Port_t) :: port
type(gov_cca_Services_t) :: services
type(SIDL_BaseInterface_t) :: throwaway
type(SIDL_BaseInterface_t) :: dumex
type(demo_Driver_wrap) :: dp
logical dr_port ! if dr_X true, the deleteRef(X) is needed before return.
type(demo_Integration_t) :: integrate__p ! non-null if specific uses port obtained.
logical integrate_fetched ! true if releaseport is needed for this port.
character (LEN=*) errMsg0_integrate
character (LEN=*) errMsg1_integrate
character (LEN=*) errMsg2_integrate
character (LEN=*) errMsg3_integrate
character (LEN=*) errMsg4_integrate
parameter(errMsg0_integrate= &
'demo.Driver: Error go() getPort(integrate) failed.')
parameter(errMsg1_integrate= &
'demo.Driver: Error casting gov.cca.Port integrate to type demo.Integration')
parameter(errMsg2_integrate= &
'demo.Driver: Error in deleteRef(port) while getting integrate')
parameter(errMsg3_integrate= &
'demo.Driver: Error calling releasePort(integrate). Continuing.')
parameter(errMsg4_integrate = &
'demo.Driver: Error in deleteRef for port integrate. Continuing.')
BOCCA_EXTERNAL
! not crashing if something fails requires good bookkeeping and exception handling.
call set_null(services)
call set_null(port)
call set_null(throwaway)
call set_null(dumex)
dr_port = .false.
bocca_status = 0
call demo_Driver__get_data_m(self,dp);
services = dp%d_private_data%d_services
if (is_null(services) ) then
call BOCCA_SIDL_THROW_F90(exception, 'NULL d_services pointer in demo.Driver.go()')
endif
! Use a demo.Integration port with port name integrate
call getPort(services,"integrate", port, throwaway)
if ( not_null(throwaway) ) then
call set_null(port)
call checkException(self, throwaway, errMsg0_integrate, .false., dumex)
! we will continue with port null (never successfully assigned) and set a flag.
endif
call set_null( integrate__p)
integrate_fetched = .false.
if ( not_null(port)) then
integrate_fetched = .true. ! even if the next cast fails, must releasePort.
call cast(port, integrate__p, exception)
BOCCA_SIDL_CHECK_F90(exception, errMsg1_integrate)
call deleteRef(port, exception)
call set_null(port)
BOCCA_SIDL_CHECK_F90(exception, errMsg2_integrate)
endif
! Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog)
! When this block is rewritten by the user, we will not change it.
! All port instances should be rechecked for NULL before calling in user code.
! Not all ports need be connected in arbitrary use.
! The port instance names used in registerUsesPort appear as local variable
! names here with the suffix __p added.
! Initialize local variables
count = 100000
lowBound = 0.0
upBound = 1.0
if (not_null(integrate__p)) then
value = -1.0 ! nonsense number to confirm it is set
! operate on the port. if successful, set the status to 0 for ok.
bocca_status = -2
call march(integrate__p, lowBound, upBound, count, value, exception)
! jump to BOCCAEXIT if an error.
BOCCA_SIDL_CHECK_F90(exception,'Driver:go: problem calling integrate')
write(*,*) 'Value = ', value
bocca_status = 0
else
bocca_status = -1 ; ! integratorPort is not connected.
write(*,*) 'Driver: integrate port not connected. connect and try again'
endif
! If unknown exceptions in the user code are tolerable and restart is ok,
! set bocca_status -1 instead.
! -2 means the component is so confused that it and probably the application
! should be destroyed.
!
BOCCAEXIT continue ! target point for normal and error cleanup. do not delete.
! Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog)
if (not_null(port)) then
call deleteRef(port,throwaway)
call checkException(self, throwaway, 'cleanup port error', .false., dumex)
call set_null(port)
endif
! release integrate
if (integrate_fetched) then
integrate_fetched = .false.
call releasePort(services, 'integrate', throwaway)
call checkException(self, throwaway, errMsg3_integrate, .false., dumex)
if ( not_null(integrate__p) ) then
call deleteRef(integrate__p, throwaway)
call checkException(self, throwaway, errMsg4_integrate, .false., dumex)
call set_null(integrate__p)
endif
endif
! Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog)
! Insert-User-Exception-Cleanup-Here
retval = bocca_status
! DO-NOT-DELETE splicer.end(demo.Driver.go)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited.
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/demo.Driver/demo_Driver_Impl.F90
Now remake your project tree to finish the components:
$
make
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # Building in ports/, languages: f90 # ======================================================================= ## Building ports... # ======================================================================= # Building in components/clients/, languages: f90 # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: f90 # ======================================================================= [s] Building class/component demo.Driver: [s] creating class/component library: libdemo.Driver.la ... [s] finished libtooling: components/demo.Driver/libdemo.Driver.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/install/share/cca/demo/demo.Driver_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Function: [s] creating class/component library: libdemo.Function.la ... [s] finished libtooling: components/demo.Function/libdemo.Function.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/install/share/cca/demo/demo.Function_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Integrator: [s] creating class/component library: libdemo.Integrator.la ... [s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/install/share/cca/demo/demo.Integrator_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.emptyComponent: doing nothing -- library is up-to-date. Build summary: SUCCESS building demo.Driver SUCCESS building demo.Function SUCCESS building demo.Integrator ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo'
It is good practice to do a make check at this point:
$
make check
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo' make --no-print-directory --no-builtin-rules -C components check ### Test library load and instantiation for the following languages: f90 Running instantiation tests only Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/tests/instantiation.gen.rc Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/tests/instantiation.gen.rc.log SUCCESS: ==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo/components/tests/instantiation.gen.rc.log). make --no-print-directory --no-builtin-rules check-user make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/f90/demo'
You should now be able to instantiate these components, assemble them into an application, and run the application, following the same procedures as in Section 2, and get a result that's reasonably close to pi .
![]()
Note
Assumes you created the project with bocca create
project demo --language=c.
Edit the evaluate method in the implementation file (also known as ``the impl'') that Bocca has generated for you (by invoking Babel ). Use the bocca edit -i to go directly to each method.
$
bocca edit -i Function evaluate
The editor opens up in the place where the implementation code for evaluate must be put. You see a default implementation generated by Babel for all user methods: the throwing of an exception which says the method is not yet implemented.
double
impl_demo_Function_evaluate(
/* in */ demo_Function self,
/* in */ double x,
/* out */ sidl_BaseInterface *_ex)
{
*_ex = 0;
{
/* DO-NOT-DELETE splicer.begin(demo.Function.evaluate) */
/* Insert-Code-Here {demo.Function.evaluate} (evaluate method) */
/*
* This method has not been implemented
*/
/* DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Function.evaluate) */
SIDL_THROW(*_ex, sidl_NotImplementedException, "This method has not been implemented");
EXIT:;
/* DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Function.evaluate) */
/* DO-NOT-DELETE splicer.end(demo.Function.evaluate) */
}
}
As the comment suggests, this method is ``not implemented'', but some
code has been inserted by Babel to make sure an exception is thrown
to inform the user if this method is called by mistake. Delete this
exception code and substitute an implementation for the
PiFunction (i.e., the integral from 0 to 1 of
4/(1 + x2)
is an approximation of
).
/* DO-NOT-DELETE splicer.begin(demo.Function.evaluate) */
return 4.0 / (1.0 + x * x);
/* DO-NOT-DELETE splicer.end(demo.Function.evaluate) */
Now in the same file just above the evaluate method, find the second method for the FunctionPort init method:
/* DO-NOT-DELETE splicer.begin(demo.Function.init) */
/* Do nothing.*/
/* DO-NOT-DELETE splicer.end(demo.Function.init) */
We don't have any initialization in this simple example, so we just eliminate the code that throws the exception when the method is executed.
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/demo.Function/demo_Function_Impl.c
Similarly edit the march method in the Integrator with
$
bocca edit -i Integrator march
double
impl_demo_Integrator_march(
/* in */ demo_Integrator self,
/* in */ double lowBound,
/* in */ double upBound,
/* in */ int32_t count,
/* out */ sidl_BaseInterface *_ex)
{
*_ex = 0;
{
/* DO-NOT-DELETE splicer.begin(demo.Integrator.march) */
/* Insert-Code-Here {demo.Integrator.march} (march method) */
/*
* This method has not been implemented
*/
/* DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Integrator.march) */
SIDL_THROW(*_ex, sidl_NotImplementedException, "This method has not been implemented");
EXIT:;
/* DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Integrator.march) */
/* DO-NOT-DELETE splicer.end(demo.Integrator.march) */
}
}
Again remove this boilerplate exception code and insert an implementation of the Trapezoid rule for integration that uses the FunctionPort :
/* DO-NOT-DELETE splicer.begin(demo.Integrator.march) */
gov_cca_Port port = NULL;
gov_cca_Services services = NULL;
sidl_BaseInterface throwaway_excpt = NULL;
sidl_BaseInterface dummy_excpt = NULL;
struct demo_Integrator__data *pd = NULL;
const char *errMsg = NULL;
double retval = 0.0;
demo_FunctionPort odeRHS = NULL;
/* odeRHS non-null if specific uses port obtained. */
int odeRHS_fetched = FALSE;
/* odeRHS_fetched true if releaseport is needed for this port. */
pd = demo_Integrator__get_data(self);
if (pd == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL object data pointer in demo.Integrator.march()");
}
services = pd->d_services;
if (services == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL pd->d_services pointer in demo.Integrator.march()");
}
/* Use a demo.Integration port with port name odeRHS */
port =
gov_cca_Services_getPort(services,"odeRHS", _ex); SIDL_CHECK(*_ex);
odeRHS_fetched = TRUE;
/* even if the next cast fails, must releasePort. */
errMsg="demo.Integrator: Error casting odeRHS to FunctionPort";
odeRHS = gov_cca_Services__cast2(port,
"demo.FunctionPort",
_ex); SIDL_CHECK(*_ex);
gov_cca_Port_deleteRef(port, _ex); port = NULL; SIDL_CHECK(*_ex);
{
double h;
double sum = 0.0;
double left, right, fvalueleft, fvalueright;
int i;
h = (upBound - lowBound) / (1.0*count);
printf("Evaluating from %g to %g by %d\n",lowBound ,upBound, count);
for (i = 1; i <= count; i++){
left = lowBound + (i - 1) * h;
fvalueleft = demo_FunctionPort_evaluate(odeRHS,
left,_ex); SIDL_CHECK(*_ex);
right = lowBound + i * h;
fvalueright = demo_FunctionPort_evaluate(odeRHS,
right,_ex); SIDL_CHECK(*_ex);
sum += (fvalueleft + fvalueright);
}
retval = h * sum/2.0;
printf("IP returning %g\n",retval);
}
EXIT:; /* target point for normal and error cleanup. do not delete. */
/* release integrate */
if (odeRHS_fetched) {
odeRHS_fetched = FALSE;
gov_cca_Services_releasePort(services,"odeRHS",&throwaway_excpt);
if ( throwaway_excpt != NULL) {
errMsg= "demo.Integrator: Error calling"
" releasePort(\"integrate\"). Continuing.";
demo_Integrator_checkException(self, throwaway_excpt, errMsg,
FALSE, &dummy_excpt);
}
if (odeRHS != NULL) {
demo_FunctionPort_deleteRef(odeRHS, &throwaway_excpt);
errMsg = "Error in demo_FunctionPort_deleteRef"
" for demo.Function port odeRHS";
demo_Integrator_checkException(self, throwaway_excpt, errMsg,
FALSE, &dummy_excpt);
odeRHS = NULL;
}
}
return retval;
/* DO-NOT-DELETE splicer.end(demo.Integrator.march) */
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/demo.Integrator/demo_Integrator_Impl.c
Finally for the Driver component we have to implement the GoPort details to get things going. Bocca will take you to the generated method, which looks like this:
$
bocca edit -i Driver go
int32_t
impl_demo_Driver_go(
/* in */ demo_Driver self,
/* out */ sidl_BaseInterface *_ex)
{
*_ex = 0;
{
/* DO-NOT-DELETE splicer.begin(demo.Driver.go) */
/* User action portion is in the middle at the next Insert-UserCode-Here line. */
/* Insert-User-Declarations-Here */
/* Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog) */
int bocca_status = 0;
/* The user's code should set bocca_status 0 if computation proceeded ok.
// The user's code should set bocca_status -1 if computation failed but might
// succeed on another call to go(), e.g. wheh a required port is not yet connected.
// The user's code should set bocca_status -2 if the computation failed and can
// never succeed in a future call.
// The users's code should NOT use return in this function;
// Exceptions that are not caught in user code will be converted to status -2.
*/
gov_cca_Port port = NULL;
gov_cca_Services services = NULL;
sidl_BaseInterface throwaway_excpt = NULL;
sidl_BaseInterface dummy_excpt = NULL;
struct demo_Driver__data *pd = NULL;
const char *errMsg = NULL;
demo_Integration integrate = NULL; /* non-null if specific uses port obtained. */
int integrate_fetched = FALSE; /* true if releaseport is needed for this port. */
pd = demo_Driver__get_data(self);
if (pd == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL object data pointer in demo.Driver.go()");
}
services = pd->d_services;
if (services == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL pd->d_services pointer in demo.Driver.go()");
}
/* Use a demo.Integration port with port name integrate */
port = gov_cca_Services_getPort(services,"integrate", &throwaway_excpt);
if (throwaway_excpt != NULL) {
port = NULL;
errMsg="go() getPort(integrate) failed.";
demo_Driver_checkException(self, throwaway_excpt, errMsg,
FALSE, &dummy_excpt);
/* we will continue with port NULL (never successfully assigned) and set a flag. */
BOCCA_FPRINTF(stderr,
"demo.Driver: Error calling getPort(\"integrate\") at %s:%d. Continuing.\n",
__FILE__ , __LINE__ -8 );
}
if ( port != NULL ) {
integrate_fetched = TRUE; /* even if the next cast fails, must releasePort. */
errMsg="demo.Driver: Error casting gov.cca.Port integrate to type demo.Integration";
integrate = demo_Integration__cast(port, _ex); SIDL_CHECK(*_ex);
gov_cca_Port_deleteRef(port,_ex); port = NULL; SIDL_CHECK(*_ex);
}
/* Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog) */
/* When this block is rewritten by the user, we will not change it.
All port instances should be rechecked for NULL before calling in user code.
Not all ports need be connected in arbitrary use.
The port instance names used in registerUsesPort appear as local variable
names here.
'return' should not be used here; set bocca_status instead.
*/
/* Insert-UserCode-Here {demo.Driver.go} */
/* BEGIN REMOVE ME BLOCK */
BOCCA_FPRINTF(stderr,
"USER FORGOT TO FILL IN THEIR GO FUNCTION %s:%d.\n",
__FILE__,__LINE__);
/* END REMOVE ME BLOCK */
/* If unknown exceptions in the user code are tolerable and restart is ok,
set bocca_status -1 instead.
-2 means the component is so confused that it and probably the component
or application should be destroyed.
*/
EXIT:; /* target point for normal and error cleanup. do not delete. */
/* Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog) */
/* release integrate */
if (integrate_fetched) {
integrate_fetched = FALSE;
gov_cca_Services_releasePort(services,"integrate",&throwaway_excpt);
if ( throwaway_excpt != NULL) {
errMsg= "demo.Driver: Error calling releasePort(\"integrate\"). Continuing.";
demo_Driver_checkException(self, throwaway_excpt, errMsg, FALSE, &dummy_excpt);
}
if (integrate != NULL) {
demo_Integration_deleteRef(integrate, &throwaway_excpt);
errMsg = "Error in demo_Integration_deleteRef for demo.Driver port integrate";
demo_Driver_checkException(self, throwaway_excpt, errMsg, FALSE, &dummy_excpt);
integrate = NULL;
}
}
/* Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog) */
/* Insert-User-Exception-Cleanup-Here */
return bocca_status;
/*
* This method has not been implemented
*/
/* DO-NOT-DELETE splicer.end(demo.Driver.go) */
}
}
Find the REMOVE block within the go method implementation, delete it, and insert the numerical logic needed to use the integrator.IntegratorPort port. Any required local variables should be inserted just before the boccaGoProlog protected block.
The go subroutine will be called by the framework when the component's run button (the name of this particular GoPort instance) is pushed in the GUI. Bocca generates the code to the Integration that the Driver is connected to. We just have to use it to compute the integral and return the proper value for bocca_status.
/* DO-NOT-DELETE splicer.begin(demo.Driver.go) */
/* User action portion is in the middle at the next Insert-UserCode-Here line. */
/* Insert-User-Declarations-Here */
/* Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog) */
int bocca_status = 0;
/* The user's code should set bocca_status 0 if computation proceeded ok.
// The user's code should set bocca_status -1 if computation failed but might
// succeed on another call to go(), e.g. wheh a required port is not yet connected.
// The user's code should set bocca_status -2 if the computation failed and can
// never succeed in a future call.
// The users's code should NOT use return in this function;
// Exceptions that are not caught in user code will be converted to status -2.
*/
gov_cca_Port port = NULL;
gov_cca_Services services = NULL;
sidl_BaseInterface throwaway_excpt = NULL;
sidl_BaseInterface dummy_excpt = NULL;
struct demo_Driver__data *pd = NULL;
const char *errMsg = NULL;
demo_Integration integrate = NULL; /* non-null if specific uses port obtained. */
int integrate_fetched = FALSE; /* true if releaseport is needed for this port. */
pd = demo_Driver__get_data(self);
if (pd == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL object data pointer in demo.Driver.go()");
}
services = pd->d_services;
if (services == NULL) {
SIDL_THROW(*_ex, sidl_SIDLException,
"NULL pd->d_services pointer in demo.Driver.go()");
}
/* Use a demo.Integration port with port name integrate */
port = gov_cca_Services_getPort(services,"integrate", &throwaway_excpt);
if (throwaway_excpt != NULL) {
port = NULL;
errMsg="go() getPort(integrate) failed.";
demo_Driver_checkException(self, throwaway_excpt, errMsg,
FALSE, &dummy_excpt);
/* we will continue with port NULL (never successfully assigned) and set a flag. */
BOCCA_FPRINTF(stderr,
"demo.Driver: Error calling getPort(\"integrate\") at %s:%d. Continuing.\n",
__FILE__ , __LINE__ -8 );
}
if ( port != NULL ) {
integrate_fetched = TRUE; /* even if the next cast fails, must releasePort. */
errMsg="demo.Driver: Error casting gov.cca.Port integrate to type demo.Integration";
integrate = demo_Integration__cast(port, _ex); SIDL_CHECK(*_ex);
gov_cca_Port_deleteRef(port,_ex); port = NULL; SIDL_CHECK(*_ex);
}
/* Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog) */
/* When this block is rewritten by the user, we will not change it.
All port instances should be rechecked for NULL before calling in user code.
Not all ports need be connected in arbitrary use.
The port instance names used in registerUsesPort appear as local variable
names here.
'return' should not be used here; set bocca_status instead.
*/
/* Insert-UserCode-Here {demo.Driver.go} */
if (integrate == NULL) {
bocca_status = -1; /* not connected. skip computation. */
} else {
int count = 100000;
double value = -4;
double lowerBound = 0.0;
double upperBound = 1.0;
fprintf(stdout, "Initvalue = %g\n", value);
value = demo_Integration_march(integrate, lowerBound, upperBound,
count, _ex); SIDL_CHECK(*_ex);
fprintf(stdout, "Value = %g\n", value);
fflush(stdout);
}
/* If unknown exceptions in the user code are tolerable and restart is ok,
set bocca_status -1 instead.
-2 means the component is so confused that it and probably the component or application
should be destroyed.
*/
EXIT:; /* target point for normal and error cleanup. do not delete. */
/* Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog) */
/* release integrate */
if (integrate_fetched) {
integrate_fetched = FALSE;
gov_cca_Services_releasePort(services,"integrate",&throwaway_excpt);
if ( throwaway_excpt != NULL) {
errMsg= "demo.Driver: Error calling releasePort(\"integrate\"). Continuing.";
demo_Driver_checkException(self, throwaway_excpt, errMsg, FALSE, &dummy_excpt);
}
if (integrate != NULL) {
demo_Integration_deleteRef(integrate, &throwaway_excpt);
errMsg = "Error in demo_Integration_deleteRef for demo.Driver port integrate";
demo_Driver_checkException(self, throwaway_excpt, errMsg, FALSE, &dummy_excpt);
integrate = NULL;
}
}
/* Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog) */
/* Insert-User-Exception-Cleanup-Here */
return bocca_status;
/* DO-NOT-DELETE splicer.end(demo.Driver.go) */
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited.
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/demo.Driver/demo_Driver_Impl.c
Now remake your project tree to finish the components:
$
make
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # Building in ports/, languages: c # ======================================================================= ## Building ports... # ======================================================================= # Building in components/clients/, languages: c # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: c # ======================================================================= [s] Building class/component demo.Driver: [s] creating class/component library: libdemo.Driver.la ... [s] finished libtooling: components/demo.Driver/libdemo.Driver.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/install/share/cca/demo/demo.Driver_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Function: [s] creating class/component library: libdemo.Function.la ... [s] finished libtooling: components/demo.Function/libdemo.Function.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/install/share/cca/demo/demo.Function_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Integrator: [s] creating class/component library: libdemo.Integrator.la ... [s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/install/share/cca/demo/demo.Integrator_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.emptyComponent: doing nothing -- library is up-to-date. Build summary: SUCCESS building demo.Driver SUCCESS building demo.Function SUCCESS building demo.Integrator ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo'
It is good practice to do a make check at this point:
$
make check
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo' make --no-print-directory --no-builtin-rules -C components check ### Test library load and instantiation for the following languages: c Running instantiation tests only Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/tests/instantiation.gen.rc Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/tests/instantiation.gen.rc.log SUCCESS: ==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo/components/tests/instantiation.gen.rc.log). make --no-print-directory --no-builtin-rules check-user make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/c/demo'
You should now be able to instantiate these components, assemble them into an application, and run the application, following the same procedures as in Section 2, and get a result that's reasonably close to pi .
![]()
Note
Assumes you created the project with bocca create
project demo --language=python.
Edit the evaluate method in the implementation file (also known as ``the impl'') that Bocca has generated for you (by invoking Babel ). Use the bocca edit -i to go directly to each method.
$
bocca edit -i Function evaluate
The editor opens up in the place where the implementation code for evaluate must be put. You see a default implementation generated by Babel for all user methods: the throwing of an exception which says the method is not yet implemented.
def evaluate(self, x):
#
# sidl EXPECTED INCOMING TYPES
# ============================
# double x
#
#
# sidl EXPECTED RETURN VALUE(s)
# =============================
# double _return
#
# DO-NOT-DELETE splicer.begin(evaluate)
#
# This method has not been implemented
#
# DO-DELETE-WHEN-IMPLEMENTING exception.begin(evaluate)
noImpl = sidl.NotImplementedException.NotImplementedException()
noImpl.setNote("This method has not been implemented.")
raise sidl.NotImplementedException._Exception, noImpl
# DO-DELETE-WHEN-IMPLEMENTING exception.end(evaluate)
# DO-NOT-DELETE splicer.end(evaluate)
As the comment suggests, this method is ``not implemented'', but some
code has been inserted by Babel to make sure an exception is thrown
to inform the user if this method is called by mistake. Delete this
exception code and substitute an implementation for the
PiFunction (i.e., the integral from 0 to 1 of
4/(1 + x2)
is an approximation of
).
# DO-NOT-DELETE splicer.begin(evaluate)
return 4.0 / (1.0 + x * x)
# DO-NOT-DELETE splicer.end(evaluate)
Now in the same file just above the evaluate method, find the second method for the FunctionPort init method:
# DO-NOT-DELETE splicer.begin(init)
# Do nothing.
pass
# DO-NOT-DELETE splicer.end(init)
We don't have any initialization in this simple example, so we just eliminate the code that throws the exception when the method is executed.
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/demo.Function/demo/Function_Impl.py
Similarly edit the march method in the Integrator with
$
bocca edit -i Integrator march
def march(self, lowBound, upBound, count):
#
# sidl EXPECTED INCOMING TYPES
# ============================
# double lowBound
# double upBound
# int count
#
#
# sidl EXPECTED RETURN VALUE(s)
# =============================
# double _return
#
# DO-NOT-DELETE splicer.begin(march)
#
# This method has not been implemented
#
# DO-DELETE-WHEN-IMPLEMENTING exception.begin(march)
noImpl = sidl.NotImplementedException.NotImplementedException()
noImpl.setNote("This method has not been implemented.")
raise sidl.NotImplementedException._Exception, noImpl
# DO-DELETE-WHEN-IMPLEMENTING exception.end(march)
# DO-NOT-DELETE splicer.end(march)
Again remove this boilerplate exception code and insert an implementation of the Trapezoid rule for integration that uses the FunctionPort :
# DO-NOT-DELETE splicer.begin(march)
# Use a demo.FunctionPort port with port name odeRHS
try:
port = self.d_services.getPort("odeRHS")
except Exception,e:
if self.bocca_print_errs:
print "demo.Integrator: port odeRHS not connected."
e.args = "demo.Integrator: port odeRHS not connected:\n%s" \
% e.args
raise
odeRHS = demo.FunctionPort.FunctionPort(port);
if not odeRHS:
if self.bocca_print_errs:
print "demo.Integrator: Error casting port gov.cca.Port " \
+ "to odeRHS type demo.FunctionPort"
ex = sidl.SIDLException.SIDLException()
ex.setNote(__name__,0,
'Error casting self Port to demo.FunctionPort')
raise sidl.SIDLException._Exception, ex
# do the math
h = (upBound - lowBound) / count
retval = 0.0
sum = 0.0
for i in range(1,count+1):
sum += odeRHS.evaluate(lowBound + (i - 1) * h)
sum += odeRHS.evaluate(lowBound + i * h)
retval = h/2.0 * sum
self.d_services.releasePort("odeRHS")
return retval
# DO-NOT-DELETE splicer.end(march)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/demo.Integrator/demo/Integrator_Impl.py
Finally for the Driver component we have to implement the GoPort details to get things going. Bocca will take you to the generated method, which looks like this:
$
bocca edit -i Driver go
def go(self):
#
# sidl EXPECTED RETURN VALUE(s)
# =============================
# int _return
#
"""\
Execute some encapsulated functionality on the component.
Return 0 if ok, -1 if internal error but component may be
used further, and -2 if error so severe that component cannot
be further used safely.
"""
# DO-NOT-DELETE splicer.begin(go)
# Bocca generated code. bocca.protected.begin(go:boccaGoProlog)
bocca_status = 0
# The user's code should set bocca_status 0 if computation proceeded ok.
# The user's code should set bocca_status -1 if computation failed but might
# succeed on another call to go(), e.g. when a required port is not yet connected.
# The user's code should set bocca_status -2 if the computation failed and can
# never succeed in a future call.
# The users's code should NOT use return in this function;
# Exceptions that are not caught in user code will be converted to status 2.
if bocca_status == 0: # skip this getport if a problem already occured.
# Use a demo.Integration port with port name integrate
try:
port = self.d_services.getPort("integrate")
except sidl.BaseException._Exception, e:
port = None
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error calling getPort('integrate'): " + eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
integrate_fetched = False;
if not port:
if self.bocca_print_errs:
print 'demo.Driver: getPort("integrate") returned nil.'
else:
integrate_fetched = True # even if the next cast fails, must release.
integrate = demo.Integration.Integration(port);
if not integrate:
bocca_status = -1
if self.bocca_print_errs:
print "demo.Driver: Error casting port gov.cca.Port to " + "integrate type demo.Integration"
if bocca_status == 0: # all is ok so far and we do the user code, else cleanup and return.
# user code indents to match this.
# Bocca generated code. bocca.protected.end(go:boccaGoProlog)
# If this try/catch block is rewritten by the user, we will not change it.
try:
try:
# The user might not require all ports to be connected in all configurations.
# Each uses port is available as the local variable with the same name.
# Those that are properly connected will be a value other than None.
# the proper test for an unavailable port is "if not portinstancename:"
# BEGIN REMOVE ME BLOCK
ex = sidl.SIDLException.SIDLException()
ex.setNote("USER FORGOT TO FILL IN THEIR FUNCTION demo.Driver.go()")
raise sidl.SIDLException._Exception, ex
# END REMOVE ME BLOCK
except sidl.BaseException._Exception, e:
bocca_status = -2
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error in go() execution: "+eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
# if specific exceptions in the user code are tolerable
# and restart is ok, bocca_status -1 instead.
# 2 means the component is so confused that it and probably
# the application should be destroyed.
except Exception,e:
bocca_status = -2
if self.bocca_print_errs:
print >> sys.stderr, 'Exception in demo.Driver go():'+str(e)
except:
bocca_status = -2
print >> sys.stderr, 'Unclassified Exception in demo.Driver go()'
finally:
# always executed.
pass
# This version of TryExceptFinally for compatibility with python 2.3 and up
# Bocca generated code. bocca.protected.begin(go:boccaGoEpilog)
# end user code.
# end if bocca_status == 0.
# release integrate
if integrate_fetched:
integrate_fetched = False
try:
self.d_services.releasePort("integrate")
except sidl.BaseException._Exception, e:
port = None
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error calling releasePort('integrate'): " + eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
return bocca_status
# Bocca generated code. bocca.protected.end(go:boccaGoEpilog)
#
# This method has not been implemented
#
# DO-NOT-DELETE splicer.end(go)
Find the REMOVE block within the go method implementation, delete it, and insert the numerical logic needed to use the integrator.IntegratorPort port. Any required local variables should be inserted just before the boccaGoProlog protected block.
The go subroutine will be called by the framework when the component's run button (the name of this particular GoPort instance) is pushed in the GUI. Bocca generates the code to the Integration that the Driver is connected to. We just have to use it to compute the integral and return the proper value for bocca_status.
# DO-NOT-DELETE splicer.begin(go)
# Bocca generated code. bocca.protected.begin(go:boccaGoProlog)
bocca_status = 0
# The user's code should set bocca_status 0 if computation proceeded ok.
# The user's code should set bocca_status -1 if computation failed but might
# succeed on another call to go(), e.g. when a required port is not yet connected.
# The user's code should set bocca_status -2 if the computation failed and can
# never succeed in a future call.
# The users's code should NOT use return in this function;
# Exceptions that are not caught in user code will be converted to status 2.
if bocca_status == 0: # skip this getport if a problem already occured.
# Use a demo.Integration port with port name integrate
try:
port = self.d_services.getPort("integrate")
except sidl.BaseException._Exception, e:
port = None
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error calling getPort('integrate'): " + eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
integrate_fetched = False;
if not port:
if self.bocca_print_errs:
print 'demo.Driver: getPort("integrate") returned nil.'
else:
integrate_fetched = True # even if the next cast fails, must release.
integrate = demo.Integration.Integration(port);
if not integrate:
bocca_status = -1
if self.bocca_print_errs:
print "demo.Driver: Error casting port gov.cca.Port to " + "integrate type demo.Integration"
if bocca_status == 0: # all is ok so far and we do the user code, else cleanup and return.
# user code indents to match this.
# Bocca generated code. bocca.protected.end(go:boccaGoProlog)
# If this try/catch block is rewritten by the user, we will not change it.
try:
try:
# The user might not require all ports to be connected in all configurations.
# Each uses port is available as the local variable with the same name.
# Those that are properly connected will be a value other than None.
# the proper test for an unavailable port is "if not portinstancename:"
count = 100000
lowerBound = 0.0
upperBound = 1.0
# operate on the port
value = integrate.march(lowerBound, upperBound, count)
print 'Value =', value
except sidl.BaseException._Exception, e:
bocca_status = -2
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error in go() execution: " \
+ eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
# if specific exceptions in the user code are tolerable
# and restart is ok, bocca_status -1 instead.
# 2 means the component is so confused that it and probably
# the application should be destroyed.
except Exception,e:
bocca_status = -2
if self.bocca_print_errs:
print >> sys.stderr, 'Exception in demo.Driver go():'+str(e)
except:
bocca_status = -2
print >> sys.stderr, 'Unclassified Exception in demo.Driver go()'
finally:
# always executed.
pass
# This version of TryExceptFinally for compatibility with python 2.3 and up
# Bocca generated code. bocca.protected.begin(go:boccaGoEpilog)
# end user code.
# end if bocca_status == 0.
# release integrate
if integrate_fetched:
integrate_fetched = False
try:
self.d_services.releasePort("integrate")
except sidl.BaseException._Exception, e:
port = None
if self.bocca_print_errs:
(etype, eobj, etb) = sys.exc_info()
msg="demo.Driver: Error calling releasePort('integrate'): " + eobj.exception.getNote()
print >>sys.stderr,'Exception:', msg
return bocca_status
# Bocca generated code. bocca.protected.end(go:boccaGoEpilog)
# DO-NOT-DELETE splicer.end(go)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited.
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/demo.Driver/demo/Driver_Impl.py
Now remake your project tree to finish the components:
$
make
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo' # ======================================================================= # No SIDL files in external/sidl, skipping build for external # ======================================================================= # ======================================================================= # Building in ports/, languages: python # ======================================================================= ## Building ports... # ======================================================================= # Building in components/clients/, languages: python # ======================================================================= ## Building clients... # ======================================================================= # Building in components/, languages: python # ======================================================================= [s] Building class/component demo.Driver: make[3]: `.gencode' is up to date. [s] creating class/component library: libdemo.Driver.la ... [s] finished libtooling: components/demo.Driver/libdemo.Driver.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/install/share/cca/demo/demo.Driver_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Function: make[3]: `.gencode' is up to date. [s] creating class/component library: libdemo.Function.la ... [s] finished libtooling: components/demo.Function/libdemo.Function.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/install/share/cca/demo/demo.Function_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.Integrator: make[3]: `.gencode' is up to date. [s] creating class/component library: libdemo.Integrator.la ... [s] finished libtooling: components/demo.Integrator/libdemo.Integrator.la ... [s] building /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/install/share/cca/demo/demo.Integrator_depl.xml ... [s] creating Ccaffeine test script (components/tests/instantiation.gen.rc)... [s] Building class/component demo.emptyComponent: make[3]: `.gencode' is up to date. doing nothing -- library is up-to-date. Build summary: SUCCESS building demo.Driver SUCCESS building demo.Function SUCCESS building demo.Integrator ### To test instantiation of successfully built components, run 'make check' ### ################ Finished building everything ################# ####### You can run some simple tests with 'make check' ####### make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo'
It is good practice to do a make check at this point:
$
make check
make[1]: Entering directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo' make --no-print-directory --no-builtin-rules -C components check ### Test library load and instantiation for the following languages: python Running instantiation tests only Test script: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/tests/instantiation.gen.rc Log file: /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/tests/instantiation.gen.rc.log SUCCESS: ==> Instantiation tests passed for all built components (see /home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo/components/tests/instantiation.gen.rc.log). make --no-print-directory --no-builtin-rules check-user make[1]: Leaving directory `/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/python/demo'
You should now be able to instantiate these components, assemble them into an application, and run the application, following the same procedures as in Section 2, and get a result that's reasonably close to pi .
![]()
Note
Assumes you created the project with bocca create
project demo --language=java.
Edit the evaluate method in the implementation file (also known as ``the impl'') that Bocca has generated for you (by invoking Babel ). Use the bocca edit -i to go directly to each method.
$
bocca edit -i Function evaluate
The editor opens up in the place where the implementation code for evaluate must be put. You see a default implementation generated by Babel for all user methods: the throwing of an exception which says the method is not yet implemented.
public double evaluate_Impl (
/*in*/ double x )
throws sidl.RuntimeException.Wrapper
{
// DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
// Insert-Code-Here {demo.Function.evaluate} (evaluate)
/*
* This method has not been implemented
*/
// DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Function.evaluate)
sidl.NotImplementedException noImpl = new sidl.NotImplementedException();
noImpl.setNote("This method has not been implmented.");
sidl.RuntimeException.Wrapper rex = (sidl.RuntimeException.Wrapper) sidl.RuntimeException.Wrapper._cast(noImpl);
throw rex;
// DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Function.evaluate)
// DO-NOT-DELETE splicer.end(demo.Function.evaluate)
As the comment suggests, this method is ``not implemented'', but some
code has been inserted by Babel to make sure an exception is thrown
to inform the user if this method is called by mistake. Delete this
exception code and substitute an implementation for the
PiFunction (i.e., the integral from 0 to 1 of
4/(1 + x2)
is an approximation of
).
// DO-NOT-DELETE splicer.begin(demo.Function.evaluate)
return 4.0 / (1.0 + x * x);
// DO-NOT-DELETE splicer.end(demo.Function.evaluate)
Now in the same file just above the evaluate method, find the second method for the FunctionPort init method:
// DO-NOT-DELETE splicer.begin(demo.Function.init)
// Do nothing.
// DO-NOT-DELETE splicer.end(demo.Function.init)
We don't have any initialization in this simple example, so we just eliminate the code that throws the exception when the method is executed.
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/java/demo/components/demo.Function/demo/Function_Impl.java
Similarly edit the march method in the Integrator with
$
bocca edit -i Integrator march
public double march_Impl (
/*in*/ double lowBound,
/*in*/ double upBound,
/*in*/ int count )
throws sidl.RuntimeException.Wrapper
{
// DO-NOT-DELETE splicer.begin(demo.Integrator.march)
// Insert-Code-Here {demo.Integrator.march} (march)
/*
* This method has not been implemented
*/
// DO-DELETE-WHEN-IMPLEMENTING exception.begin(demo.Integrator.march)
sidl.NotImplementedException noImpl = new sidl.NotImplementedException();
noImpl.setNote("This method has not been implmented.");
sidl.RuntimeException.Wrapper rex = (sidl.RuntimeException.Wrapper) sidl.RuntimeException.Wrapper._cast(noImpl);
throw rex;
// DO-DELETE-WHEN-IMPLEMENTING exception.end(demo.Integrator.march)
// DO-NOT-DELETE splicer.end(demo.Integrator.march)
Again remove this boilerplate exception code and insert an implementation of the Trapezoid rule for integration that uses the FunctionPort :
// DO-NOT-DELETE splicer.begin(demo.Integrator.march)
gov.cca.Port port = null;
port = this.d_services.getPort("odeRHS");
demo.FunctionPort odeRHS;
odeRHS = ( demo.FunctionPort.Wrapper )
demo.FunctionPort.Wrapper._cast((gov.cca.Port.Wrapper)port);
if (odeRHS == null) {
if (bocca_print_errs) {
System.err.println("demo.Integrator: Error casting gov.cca.Port"
+ " odeRHS to type demo.FunctionPort");
}
sidl.SIDLException ex = new sidl.SIDLException();
sidl.SIDLException.Wrapper msg = (sidl.SIDLException.Wrapper)
sidl.SIDLException.Wrapper._cast(ex);
throw msg;
}
double h = (upBound - lowBound) / count;
double retval = 0.0;
double sum = 0.0;
for (int i = 1; i <= count; i++){
sum += odeRHS.evaluate(lowBound + (i - 1) * h) +
odeRHS.evaluate(lowBound + i * h);
}
retval = h/2.0 * sum;
this.d_services.releasePort("odeRHS");
return retval;
// DO-NOT-DELETE splicer.end(demo.Integrator.march)
After quitting the editor the state of the source code tree is updated if there are any dependencies on the edited implementation. Usually there are no dependencies on the implementation file, so Bocca does very little after you exit the editor and all you see is the information from the edit command about what file was edited:
/home/bernhold/bassi/project/projectdirs/cca/pde-hands-on/doc/scratch/java/demo/components/demo.Integrator/demo/Integrator_Impl.java
Finally for the Driver component we have to implement the GoPort details to get things going. Bocca will take you to the generated method, which looks like this:
$
bocca edit -i Driver go
public int go_Impl ()
throws sidl.RuntimeException.Wrapper
{
// DO-NOT-DELETE splicer.begin(demo.Driver.go)
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoProlog)
int bocca_status = 0;
// The user's code should set bocca_status 0 if computation proceeded ok.
// The user's code should set bocca_status -1 if computation failed but might
// succeed on another call to go(), e.g. wheh a required port is not yet connected.
// The user's code should set bocca_status -2 if the computation failed and can
// never succeed in a future call.
// The users's code should NOT use return in this function;
// Exceptions that are not caught in user code will be converted to status 2.
gov.cca.Port port = null;
boolean integrate_fetched = false;
if (bocca_status == 0) { // skip further getports if problem occurs.
// Use a demo.Integration port with port name integrate, unless we've hit
// a problem already.
try{
port = this.d_services.getPort("integrate");
} catch ( gov.cca.CCAException.Wrapper ex ) {
// we will continue with port nil (never successfully assigned) and set a flag.
if (bocca_print_errs) {
System.err.println("Error calling getPort(\"integrate\")"
+ ex.getNote());
System.err.println("Continuing without integrate");
}
}
demo.Integration integrate;
if ( port != null ) {
integrate_fetched = true; // even if the next cast fails, must release.
integrate = ( demo.Integration.Wrapper )
demo.Integration.Wrapper._cast((gov.cca.Port.Wrapper)port);
if (integrate == null) {
if (bocca_print_errs) {
System.err.println("demo.Driver: Error casting gov.cca.Port "
+ "integrate to type demo.Integration");
}
bocca_status = -1;
}
}
}
if (bocca_status == 0) {
// skip user code if we already have an unexpected error. go to cleanup.
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoProlog)
// If this try/catch block is rewritten by the user, we will not change it.
try {
// All port instances may be rechecked for null before calling in user code.
// Java will throw a null object exception when using the port if it's null.
// The port instance names used in registerUsesPort appear as local variable
// names here.
// Insert-UserCode-Here {demo.Driver.go}
// BEGIN REMOVE ME BLOCK
sidl.SIDLException ex = new sidl.SIDLException();
ex.setNote("USER FORGOT TO FILL IN THEIR FUNCTION demo.Driver.go()");
sidl.BaseException.Wrapper bex =
(sidl.BaseException.Wrapper)sidl.BaseException.Wrapper._cast(ex);
throw bex;
// END REMOVE ME BLOCK
} catch (sidl.BaseException.Wrapper ex) {
bocca_status = -2;
if (bocca_print_errs) {
System.err.println("SIDL Exception in user go code: "+ ex.getNote() );
System.err.println("Returning 2 from go()");
}
} catch (java.lang.Exception jex) {
bocca_status = -2;
if (bocca_print_errs) {
if (((sidl.BaseInterface)jex).isType("sidl.BaseException")) {
System.err.println("sidl Exception in user go code: "
+ ((sidl.BaseException)jex).getNote() );
} else {
System.err.println("java Exception in user go code: "+ jex.getMessage());
}
System.err.println("Returning 2 from go()");
}
// If unknown exceptions in the user code are tolerable and restart is ok,
// use bocca_status -1 instead.
// 2 means the component is so confused that it and probably the application
// should be destroyed.
}
// Bocca generated code. bocca.protected.begin(demo.Driver.go:boccaGoEpilog)
} // cleanup
// release integrate
if (integrate_fetched) {
integrate_fetched = false;
try{
this.d_services.releasePort("integrate");
} catch ( gov.cca.CCAException.Wrapper ex ) {
if (bocca_print_errs) {
System.err.println("demo.Driver: Error calling "
+ "releasePort(\"integrate\"): " + ex.getNote());
}
}
// java port reference will be dropped when function exits,
// but we must tell framework.
}
return bocca_status;
// Bocca generated code. bocca.protected.end(demo.Driver.go:boccaGoEpilog)
/*
* This method has not been implemented
*/
// DO-NOT-DELETE splicer.end(demo.Driver.go)
Find the REMOVE block within the go method implementation, delete it, and insert the numerical logic needed to use the integrator.IntegratorPort port. Any required local variables should be inserted just before the boccaGoProlog protected block.
The go subroutine will be called by the framework when the component's run button (the name of this particular GoPort instance) is pushed in the GUI. Bocca generates the code to the Integration that the Driver is connected to. We just have to use it to compute the integral and return the proper value for bocca_status.