You'll need to have the Scripting Chapter of the Component Builder's Manual at hand for clarifications on syntax and execution semantics.
path("/opt/orocos/lib/orocos") // Path to where components are located [1] import("myproject") // imports a specific project in the path [2] import("ocl") // imports ocl from the path require("print") // loads the 'print' service globally. [3] loadComponent("HMI1","OCL::HMIComponent") // create a new HMI component [4] loadComponent("Controller1","MyProjectController") // create a new controller loadComponent("Test1","TaskContext") // creates an empty test component
You can test this code by doing:
deployer-gnulinux -s startup.ops
deployer-gnulinux ... Deployer [S]> help runScript runScript( string const& File ) : bool Runs a script. File : An Orocos program script. Deployer[S]> runScript("startup.ops")
The first line of startup.ops ([1]) extends the standard search path for components. Every component library directly in a path will be discovered using this statement, but the paths are not recursively searched. For loading components in subdirectories of a path directory, use the import statement. In our example, it will look for the myproject directory in the component paths and the ocl directory. All libraries and plugins in these directories will be loaded as well.
After importing, we can create components using loadComponent ([4]). The first argument is the name of the component instance, the second argument is the class type of the component. When these lines are executed, 3 new components have been created: HMI1, Controller1 and Test1.
Finally, the line require("print") loads the printing service globally such that your script can use the 'print.ln("text")' function. See help print in the TaskBrowser after you typed require("print").
Now extend the script to include the lines below. The create connection policy objects and connect ports between components.
// See the Doxygen API documentation of RTT for the fields of this struct: var ConnPolicy cp_1 // set the fields of cp_1 to an application-specific value: cp_1.type = BUFFER // Use ''BUFFER'' or ''DATA'' cp_1.size = 10 // size of the buffer cp_1.lock_policy = LOCKED // Use ''LOCKED'', ''LOCK_FREE'' or ''UNSYNC'' // other fields exist too... // Start connecting ports: connect("HMI1.positions","Controller1.positions", cp_1) cp_1 = ConnPolicy() // reset to defaults (DATA, LOCK_FREE) connect("HMI1.commands","Controller1.commands", cp_1) // etc...
Connecting data ports is done using ConnPolicy structs that describe the properties of the connection to be formed. You may re-use the ConnPolicy variable, or create new ones for each connection you form. The Component Builder's Manual has more details on how the ConnPolicy struct influences how connections are configured.
Finally, we configure and start our components:
if ( HMI1.configure() == false ) print.ln("HMI1 configuration failed!") else { if ( Controller1.configure() == false ) print.ln("Controller1 configuration failed!") else { HMI1.start() Controller1.start() } }
StateMachine SetupShutdown { var bool do_cleanup = false, could_config = false; initial state setup { entry { // Configure components could_config = HMI1.configure() && Controller1.configure(); if (could_config) { HMI1.start(); Controller1.start(); } } transitions { if do_cleanup then select shutdown; if could_config == false then select failure; } } state failure { entry { print.ln("Failed to configure a component!") } } final state shutdown { entry { // Cleanup B group HMI1.stop() ; Controller1.stop(); HMI1.cleanup() ; Controller1.cleanup(); } } } RootMachine SetupShutdown deployApp; deployApp.activate() deployApp.start()
State machines are explained in detail in the Scripting Chapter of the Component Builder's Manual.