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.
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-97Created. */ // Include Files #include // exit() #include // 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 [at] cme.nist.gov ()