Table of Contents
Defining a New Class for a Control Module
List of NML_MODULE Member Functions and Variables
NML_MODULE is a base class for RCS control modules that primarily use NML for communications. This has sometimes been referred to as the RCS Template, although it is not defined with the C++ keyword template. It is used by deriving an application specific class from NML_MODULE and overloading some of the functions. The NML_MODULE provides each module with a consistent structure and performs some of the common chores such as measuring performance, initializing NML and checking for new commands. Many of the tasks for setting up a module can also be performed automatically with the RCS Design Tool.
In order for the main program to create an instance of this module, it needs to have a definition of the class in a C++ header file. The following provides an example: /* mcps.hpp This C++ header file defines the class MCPS_MODULE It was generated with the RCS-Design tool. MODIFICATIONS: 1-Dec-97 Modified by RCS-Design tool. 21-Oct-97 Created by RCS-Design tool. */ // Prevent Multiple Inclusion #ifndef MCPS_HPP #define MCPS_HPP // Include Files #include "rcs.hh" // Common RCS definitions #include "nml_mod.hh" // NML_MODULE definitions #include "mcpsn.hpp" // NML Commands and Status definitions for mcps #include "mcexmon.hpp" // NML Commands and Status definitions for mcexmo #include "mcexton.hpp" // NML Commands and Status definitions for mcexto // Auxilliary Input NML Message Files #include "mcps_auxinn.hpp" // NML Messages for mcps_auxin // Auxilliary Output NML Message Files #include "mcps_auxoutn.hpp" // NML Messages for mcps_auxout class MCPS_MODULE: public NML_MODULE { // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. public: MCPS_MODULE(); // Constructor // Overloaded Virtual Functions void PRE_PROCESS(); void DECISION_PROCESS(); void POST_PROCESS(); // Command Functions void INIT(MCPS_INIT *); void HALT(MCPS_HALT *); void HOME(MCPS_HOME *); void MOVE(MCPS_MOVE *); void CUT(MCPS_CUT *); // Convenience Variables MCPS_STATUS mcps_status; int mcexmo_sub_num; MCEXMO_STATUS *mcexmo_status; int mcexto_sub_num; MCEXTO_STATUS *mcexto_status; // Auxiliary Input NML Channels NML *MCPS_AUXIN_CHANNEL; // NML Channel for mcps_auxin MCPS_AUXIN_MSG *mcps_auxin_data; // NML Data for mcps_auxin // Auxiliary Output NML Channels NML *MCPS_AUXOUT_CHANNEL; // NML Channel for mcps_auxout MCPS_AUXOUT_MSG mcps_auxout_data; //NML Data for mcps_auxout // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. private: // Add custom variables and functions here. }; #endif // MCPS_HPP
The header needs to include seven other C++ header files. "rcs.hh" contains definitions for most of the utilities in the RCS Library. " nml_mod.hh" contains the base class NML_MODULE. "mcpsn.hpp" contains definitions for the NML interface to this module, the classes that can be sent as commands such as MCPS_INIT, MCPS_HALT, MCPS_HOME, MCPS_CUT, and MCPS_MOVE and the class used to provide status to its superior MCPS_STATUS. "mcexmon.hpp" and "mcexton.hpp" provide the NML messages for the two subordinates mcexmo and mcexto. "mcps_auxinn.hpp" provides the message MCPS_AUXIN_MSG read from the auxiliary input channel. "mcps_auxoutn.hpp" provides the message MCPS_AUXOUT_MSG sent to the auxiliary output channel.
The functions PRE_PROCESS, DECISION_PROCESS, and POST_PROCESS are defined in the NML_MODULE base class but typically overloaded in each control module. These functions are called every cycle by the NML_MODULE::controller function which is not normally overloaded. (RCS uses cyclic processing, normally with a constant cycle time such as 30 ms set as a parameter to the RCS_TIMER constructor in the main loop.)
PRE_PROCESS is normally used for retrieving inputs and performing simple conversions needed every cycle and by multiple commands. For example, one might read an encoder input and multiply by a scale factor to convert encoder ticks to mm. For more complicated sensor processing, it may be better to develop a separate module just for sensory processing or even an entire hierarchy. It can also be used to read any auxiliary input.
DECISION_PROCESS usually calls one of the command functions based on the current command type. The command functions for this module are INIT, HALT, MOVE, CUT and HOME.
POST_PROCESS is normally used for writing outputs and performing simple conversions on the outputs needed every cycle by multiple commands. For example, one might multiply a commanded velocity by a scale factor before writing a voltage to a DAC (Digital-to-Analog Converter). It can also be used to write any auxiliary output.
The command functions usually contain state tables made up of multiple "if ... else if ... " blocks. The state tables are designed so that only a small amount of processing needs to occur each cycle and long complex commands are spread over many cycles. The current line in the state table is the first line where the conditions for the if are true. This line is highlighted within the RCS Diagnostics tool.
The variable mcps_status will be passed as a parameter to NML_MODULE::setStatChannel inside the constructor. Then it will be sent to the NML channel every cycle. Information can be sent to the superior just by defining the appropriate variable in MCPS_STATUS and setting it in one of the command functions, PRE_PROCESS or POST_PROCESS.
The variable mcexmo_sub_num recieves the subordinate number returned by NML_MODULE::addSubordinate() that is needed by the function sendCommand() to send commands to the mcexmo module. The variable mcexmo_status is set to point to the buffer where status data from the mcexmo module will be updated every cycle.
The comments "// RCS-Design-MERGE-DISABLE" and "// RCS-Design-MERGE-ENABLE" separate out an area of the file users of the RCS-Design tool may wish to avoid editing, since those edits will be deleted if the file is modified with the RCS Design tool.
The following are segments from the actual code for the MCPS control module. (Click here to see the complete file, "mcps.cc".)
// Constructor MCPS_MODULE::MCPS_MODULE() { // Set up NML Channels // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. setErrorLogChannel(new NML(nmlErrorFormat, "errlog", "mcps", "isam.nml")); setCmdChannel(new RCS_CMD_CHANNEL(mcpsFormat, "mcps_cmd", "mcps", "isam.nml")); setStatChannel(new RCS_STAT_CHANNEL(mcpsFormat, "mcps_sts", "mcps", "isam.nml"), &mcps_status); mcexmo_sub_num = addSubordinate( new RCS_CMD_CHANNEL(mcexmoFormat, "mcexmo_cmd", "mcps", "isam.nml"), new RCS_STAT_CHANNEL(mcexmoFormat, "mcexmo_sts", "mcps", "isam.nml")); mcexmo_status = (MCEXMO_STATUS *) statusInData[mcexmo_sub_num]; mcexto_sub_num = addSubordinate( new RCS_CMD_CHANNEL(mcextoFormat, "mcexto_cmd", "mcps", "isam.nml"), new RCS_STAT_CHANNEL(mcextoFormat, "mcexto_sts", "mcps", "isam.nml")); mcexto_status = (MCEXTO_STATUS *) statusInData[mcexto_sub_num]; // Auxiliary Input NML Channels //mcps_auxin MCPS_AUXIN_CHANNEL = new NML(mcps_auxinFormat, "mcps_auxin", "mcps", "isam.nml"); mcps_auxin_data = (MCPS_AUXIN_MSG *) MCPS_AUXIN_CHANNEL->get_address(); // Auxilliary Output NML Channels //mcps_auxout MCPS_AUXOUT_CHANNEL = new NML(mcps_auxoutFormat, "mcps_auxout", "mcps", "isam.nml"); // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. // Add additional code to initialize the module here. }
The constructor is used to setup the NML channels for the module as well as any other initialization that needs to be done. The command, status and error log channels for this subordinate are setup with the functions NML_MODULE::setCmdChannel(), NML_MODULE::setStatChannel(), and NML_MODULE::setErrorLogChannel. The connections to each subordinate are setup with NML_MODULE::addSubordinate(). Finally, any auxiliary inputs or outputs are setup.
/* DECISION_PROCESS The DECISION_PROCESS function is called every cycle as long as there is a non-zero command. It is expected to call a command function based on commandInData->type. */ void MCPS_MODULE::DECISION_PROCESS() { switch(commandInData->type) { // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. case MCPS_INIT_TYPE: INIT((MCPS_INIT *)commandInData); break; case MCPS_HALT_TYPE: HALT((MCPS_HALT *)commandInData); break; case MCPS_HOME_TYPE: HOME((MCPS_HOME *)commandInData); break; case MCPS_MOVE_TYPE: MOVE((MCPS_MOVE *)commandInData); break; case MCPS_CUT_TYPE: CUT((MCPS_CUT *)commandInData); break; // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. default: logError("The command %d is not recognized.",commandInData->type); break; } }
The DECISION_PROCESS function calls the appropriate command function based on the current command type and passes commandInData to that function.
/* INIT Parameter(s): MCPS_INIT *cmd_in -- NML Message sent from superior. Most Modules will have an INIT command. The INIT function is expected to initialize any variables that may be in an uninitialized or unknown state, send INIT commands to its subordinates, wait for the subordinates to be DONE and then inform its superior that it is done The state tables should use the STATE_MATCH macro so the diagnostics tool can highlight the current line in the state table. */ void MCPS_MODULE::INIT(MCPS_INIT *cmd_in) { // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. MCEXMO_INIT mcexmoInitMsg; MCEXTO_INIT mcextoInitMsg; // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. if(STATE_MATCH(NEW_COMMAND)) { // Send an INIT command to all subordinates. // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. sendCommand(&mcexmoInitMsg, mcexmo_sub_num); sendCommand(&mcextoInitMsg, mcexto_sub_num); // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. stateNext(S1); // Reinitialize variables here. } // Wait for all subordinates to report done. // RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool. else if(STATE_MATCH(S1, mcexmo_status->status == RCS_DONE && mcexto_status->status == RCS_DONE && 1)) // RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool. { status = RCS_DONE; stateNext(S2); } else if(STATE_MATCH(S2)) { // Idle State } } The INIT function provides a good example for a command function. When a new command is received, the state is set to NEW_COMMAND. On the first cycle, the state table sends commands to its subordinates, mcexmo and mcexto, with the sendCommand function, sets the state to S1 and returns. The next cycle the state table checks to if the status of both mcexmo and mcexto is RCS_DONE. If so the state table sets its own status to RCS_DONE and state to S2.
In order to run the module it is necessary to call its controller() function from the main function for the program. In addition, the programmer has the option of combining several modules in the same executable with eliminates some of the task switching and mutual exclusion overhead and allows for tighter synchronization. It is a good idea to keep the code in this file to a minimum so that the application can be reorganized easily.
The following is an example:
/* isammain.cc This file provides the C++ main function which creates and runs the following control modules: MOTION_MODULE MCWM_MODULE MCEXTO_MODULE MCVJ_MODULE MCSP_MODULE MCPS_MODULE MCPL_MODULE MCEXMO_MODULE TOOL_MODULE MODIFICATIONS: 1-Dec-97 Created. */ // Include Files #include <stdlib.h> // exit() #include <signal.h> // SIGINT, signal() #include "rcs.hh" // Common RCS definitions #include "nml_mod.hh" // NML_MODULE definitions #include "motion.hpp" // definition of MOTION_MODULE #include "mcwm.hpp" // definition of MCWM_MODULE #include "mcexto.hpp" // definition of MCEXTO_MODULE #include "mcvj.hpp" // definition of MCVJ_MODULE #include "mcsp.hpp" // definition of MCSP_MODULE #include "mcps.hpp" // definition of MCPS_MODULE #include "mcpl.hpp" // definition of MCPL_MODULE #include "mcexmo.hpp" // definition of MCEXMO_MODULE #include "tool.hpp" // definition of TOOL_MODULE // flag signifying main loop is to terminate int isam_done = 0; //signal handler for ^C extern "C" void isam_quit(int sig); void isam_quit(int sig) { isam_done = 1; } // main loop, running 9 controller(s) int main(int argc, char **argv) { set_rcs_print_destination(RCS_PRINT_TO_STDOUT); RCS_TIMER *timer = new RCS_TIMER(0.1); MOTION_MODULE *motion = new MOTION_MODULE(); MCWM_MODULE *mcwm = new MCWM_MODULE(); MCEXTO_MODULE *mcexto = new MCEXTO_MODULE(); MCVJ_MODULE *mcvj = new MCVJ_MODULE(); MCSP_MODULE *mcsp = new MCSP_MODULE(); MCPS_MODULE *mcps = new MCPS_MODULE(); MCPL_MODULE *mcpl = new MCPL_MODULE(); MCEXMO_MODULE *mcexmo = new MCEXMO_MODULE(); TOOL_MODULE *tool = new TOOL_MODULE(); // set the SIGINT handler signal(SIGINT, isam_quit); // enter main loop while(!isam_done) { motion->controller(); mcwm->controller(); mcexto->controller(); mcvj->controller(); mcsp->controller(); mcps->controller(); mcpl->controller(); mcexmo->controller(); tool->controller(); timer->wait(); } // Delete Modules delete motion; delete mcwm; delete mcexto; delete mcvj; delete mcsp; delete mcps; delete mcpl; delete mcexmo; delete tool; // Delete Timer delete timer; }
List of NML_MODULE Member Functions and Variables
The functions and variables for NML_MODULE are listed here in alphabetical order.
Last Modified: %H%
If you have questions or comments regarding this page or you would like to be notified of changes to the RCS library via email, please contact
Will Shackleford at shackle@cme.nist.gov