BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000EE" VLINK="551A8B" ALINK="#FF0000" >
RCS Tools Walk-through (UNIX-Version)
Introduction
The Real-Time Control System (RCS) tools include C++ class libraries, Java Programs, scripts, and makefiles. This walk-through provides step by step instructions to build a sample application and test it using the RCS tools. If you have any problems following these instructions, please contact Will Shackleford. The example will be a single axis machine tool controller.
You may also download the example controller instead of building it yourself at ftp://isdftp.cme.nist.gov/pub/emc/rcslib/rcs_ex.tar.Z
- If you have not already done so, install the Java Developer's Kit and the RCS Library. The Java Developer's Kit can be obtained from http://www.javasoft.com/. You will need both the C++ and Java versions of the RCS Library for this exercize. Go to http://isd.cme.nist.gov/proj/rcs_lib/getrcs.html for instructions on downloading and installing the RCS Library. ( NOTE for NIST ISD Staff: The RCS Library is installed in /proj/rcslib and the Java Developer's Kit is installed in /depot/jdk. )
- Make a directory called rcs_ex and cd to it.
- Double Click on the RCS-Design shortcut, which should bring up this:

- This is the options screen of the RCS-Design tool. Under Java Command replace "java" with the complete path to the Java Runtime program. Under "Java Setup" add "SET CLASSPATH=" and the complete path to the classes.zip file. Switch to the Hierarchy Screen by selecting "Hierarchy" from the drop-down list in the upper-left corner which currently says "Options". The screen should look like this:

- In the text field under "Add Module", type "emove" and press enter. Do the same for prim and tool. Select the prim module, and add servo. When your done the hierarchy should look like this:

- In this example, we are building a controller for a machine tool. The emove level will accept commands to drill to a certain position or to change the drill bit. The tool controller will control the hardware necessary to change the drill bit once the position is moved back to zero. The prim controller will break down large moves from the emove level into small increments to send commanded positions to the servo module. The servo module will modify the output to a motor to make the actual position track the commanded position as closely as possible.
- Select the emove module. Select "Commands" from the drop-down list in the middle of the left side that currently says "Subordinates". Under "Add Command", type "goto" and press enter. Add the command "change_tool". The screen should look like this:

- Select the prim module and add the command "goto", select the tool module and add the command "change_tool", select the servo module and add the command "goto".
- Select "Loops" from the drop-down list in the upper left that currently says "Hierarchy". The screen should look like this:

- By default the tool assigns all the modules to the same loop at the same cycle time. However I want to run the servo module 5 times faster than the others, so I need to assign it to its own loop. Type "servo" under "Add Main Loop" and press enter. With rcs_ex selected under Main Loops click servo under "Modules In Loop" so that it will not be selected. Select servo under "Main Loops", then under "Modules In Loop" select servo. With servo selected under "Main Loops", enter 0.02 under "Cycle Time".
- To check that the last step was correct. Select "rcs_ex" under "Main Loops". Under "Modules In Loop", emove, tool, and prim should be selected but servo should not and the cycle time should be 0.1. The screen should look like this:

- Select "servo" under "Main Loops". Under "Modules In Loop" only servo should be selected. The cycle time should be 0.02. The screen should look like this:

- Press the button that says "Create Source" to create the first version of our controller. Wait until a dialog box says, "RCS Application Created" then click on OK.
- Select "Files" from the drop-down list in the upper-left. Select "src/intf/emoven.hpp" from the list of "Application Files". The screen should look like this:

- Emoven.hpp is a C++ header file that defines the messages that the emove module will accept as commands or provide as status. To add some variables to the status message emove provides to the rest of the world paste the bold lines into the text area on the right. (When I copied from Internet Explorer half the new lines were lost. If you have Netscape handy, you may want to switch to it.)
class EMOVE_STATUS : public RCS_STAT_MSG
{
public:
//Constructor
EMOVE_STATUS();
// CMS Update Function
void update(CMS *);
// Place custom variables here.
int tool;
double position;
};
- Add the bold lines to the command messages so we will be able to tell the emove level where to go, or which tool to load.
class EMOVE_GOTO : public RCS_CMD_MSG
{
public:
//Constructor
EMOVE_GOTO();
// CMS Update Function
void update(CMS *);
// Place custom variables here.
double position;
};
class EMOVE_CHANGE_TOOL : public RCS_CMD_MSG
{
public:
//Constructor
EMOVE_CHANGE_TOOL();
// CMS Update Function
void update(CMS *);
// Place custom variables here.
int tool;
};
- Click the "Save" button to save your changes.
- Select the file "src/intf/primn.hpp" from the "Application Files" list and add the variable "double position;" to PRIM_STATUS and PRIM_GOTO.
- Select the file "src/intf/servon.hpp" from the "Application Files" list and add the variable "double position;" to SERVO_STATUS and SERVO_GOTO.
- Select the file "src/intf/tooln.hpp" from the "Application Files" list and add the variable "int tool;" to TOOL_STATUS and TOOL_GOTO.
- Select the file "src/emove/emove.cpp" from the "Application Files" list. Find the function EMOVE_MODULE::GOTO. This function will be called after the emove message recieves the EMOVE_GOTO command, and again every 100 milliseconds after that until the command is complete. Paste the bold text below into the function. This state table will send PRIM a PRIM_GOTO command and then wait for prim to be done before reporting that it is done.
void EMOVE_MODULE::GOTO(EMOVE_GOTO *cmd_in)
{
// Put state table for EMOVE_GOTO here.
PRIM_GOTO primGoToMsg;
primGoToMsg.position = cmd_in->position;
if(STATE_MATCH(NEW_COMMAND))
{
sendCommand(&primGoToMsg, prim_sub_num);
stateNext(S1);
}
else if(STATE_MATCH(S1,
prim_status->status == RCS_DONE))
{
status = RCS_DONE;
stateNext(S2);
}
else if(STATE_MATCH(S2))
{
// Idle State
}
}
- Find the function EMOVE_MODULE::CHANGE_TOOL. Paste the bold text below into the function. This state table will first send prim a PRIM_GOTO with position zero and then send TOOL_CHANGE_TOOL to the tool module.
void EMOVE_MODULE::CHANGE_TOOL(EMOVE_CHANGE_TOOL *cmd_in)
{
// Put state table for EMOVE_CHANGE_TOOL here.
PRIM_GOTO primGoToMsg;
TOOL_CHANGE_TOOL toolChangeToolMsg;
primGoToMsg.position = 0.0;
toolChangeToolMsg.tool = cmd_in->tool;
if(STATE_MATCH(NEW_COMMAND))
{
sendCommand(&primGoToMsg, prim_sub_num);
stateNext(S1);
}
else if(STATE_MATCH(S1,
prim_status->status == RCS_DONE))
{
sendCommand(&toolChangeToolMsg, tool_sub_num);
stateNext(S2);
}
else if(STATE_MATCH(S2,
tool_status->status == RCS_DONE))
{
status = RCS_DONE;
stateNext(S3);
}
else if(STATE_MATCH(S3))
{
// Idle State
}
}
- Find the function EMOVE_MODULE::POST_PROCESS. The post process function is called every cycle (100 ms) after the command functions regardless of the current command or status. This will copy the status variables tool and position from the status messages of prim and tool. Paste the bold text below into the function.
void EMOVE_MODULE::POST_PROCESS()
{
// Post-Processing Code
emove_status.position = prim_status->position;
emove_status.tool = tool_status->tool;
}
Save your changes to emove.cpp by clicking the "Save" button.
Select "src/tool/tool.cpp" from the "Application Files" list. Find the function TOOL_MODULE::CHANGE_TOOL. Paste the bold text below into the function. This just simulates the rest of the tool changer by updating the status value to match the commanded tool.
void TOOL_MODULE::CHANGE_TOOL(TOOL_CHANGE_TOOL *cmd_in)
{
// Put state table for TOOL_CHANGE_TOOL here.
if(STATE_MATCH(NEW_COMMAND))
{
tool_status.tool = cmd_in->tool;
status = RCS_DONE;
stateNext(S1);
}
else if(STATE_MATCH(S1))
{
// idle state
}
}
- Select "src/prim/prim.cpp" from the "Application Files" list Paste the bold text below into the appropriate functions. The PRIM_GOTO state table should keep sending SERVO_GOTO messages each one INCREMENT closer to the goal until servo is within INCREMENT and then it passes on the final position.
void PRIM_MODULE::GOTO(PRIM_GOTO *cmd_in)
{
// Put state table for PRIM_GOTO here.
SERVO_GOTO servoGoToMsg;
const double INCREMENT = 0.1;
if(STATE_MATCH(NEW_COMMAND))
{
if( cmd_in->position - servo_status->position > INCREMENT)
{
servoGoToMsg.position = servo_status->position+INCREMENT;
stateNext(S1);
}
else if( cmd_in->position - servo_status->position < -INCREMENT)
{
servoGoToMsg.position = servo_status->position-INCREMENT;
stateNext(S1);
}
else
{
servoGoToMsg.position = cmd_in->position;
stateNext(S2);
}
sendCommand(&servoGoToMsg, servo_sub_num);
}
else if(STATE_MATCH(S1,
servo_status->status == RCS_DONE))
{
if( cmd_in->position - servo_status->position > INCREMENT)
{
servoGoToMsg.position = servo_status->position+INCREMENT;
}
else if( cmd_in->position - servo_status->position < -INCREMENT)
{
servoGoToMsg.position = servo_status->position-INCREMENT;
}
else
{
servoGoToMsg.position = cmd_in->position;
stateNext(S2);
}
sendCommand(&servoGoToMsg, servo_sub_num);
}
else if(STATE_MATCH(S2,
servo_status->status == RCS_DONE))
{
status = RCS_DONE;
stateNext(S3);
}
else if(STATE_MATCH(S3))
{
// idle state
}
}
// . . .
void PRIM_MODULE::POST_PROCESS()
{
// Post-Processing Code
prim_status.position = servo_status->position;
}
Select "src/servo/servo.hpp" from the "Application Files" list. Paste the bold text below into the the SERVO_MODULE definition. This adds some additional variables that will be preserved across mutiple cycles.
class SERVO_MODULE: public NML_MODULE
{
// RCS-Design-MERGE-DISABLE Edits to the following area will NOT be preserved by the RCS-Design tool.
public:
SERVO_MODULE(); // Constructor
// Overloaded Virtual Functions
void PRE_PROCESS();
void DECISION_PROCESS();
void POST_PROCESS();
// Command Functions
void INIT(SERVO_INIT *);
void HALT(SERVO_HALT *);
void GOTO(SERVO_GOTO *);
// Convenience Variables
SERVO_STATUS servo_status;
// RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool.
private:
// Add custom variables and functions here.
int output;
double velocity;
double commanded_position;
double actual_position;
};
- Select "src/servo/servo.cpp" from the "Application Files" list. Paste the bold text below into the appropriate functions. The constructor is used to initialize everything to a known state. The plant is simulated in PRE_PROCESS by assuming the velocity is proportional to the output to the motor. The output to the motor is computed in POST_PROCESS. Add #include <math.h> near the top of the file with the other includes for the definition of the fabs function.
SERVO_MODULE::SERVO_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", "servo", "rcs_ex.nml"));
setCmdChannel(new RCS_CMD_CHANNEL(servoFormat, "servo_cmd", "servo", "rcs_ex.nml"));
setStatChannel(new RCS_STAT_CHANNEL(servoFormat, "servo_sts", "servo", "rcs_ex.nml"), &servo_status);
// RCS-Design-MERGE-ENABLE Edits after this line will be preserved by the RCS-Design tool.
// Add additional code to initialize the module here.
output = 0;
velocity = 0.0;
commanded_position = 0.0;
actual_position = 0.0;
}
// . . .
void SERVO_MODULE::PRE_PROCESS()
{
// Pre-Processing Code
velocity = (double) output / 100.0;
actual_position += (velocity*last_cycle_time);
}
// . . .
void SERVO_MODULE::POST_PROCESS()
{
// Post-Processing Code
output = (int) ((commanded_position - actual_position)*100.0);
servo_status.position = actual_position;
}
// . . .
void SERVO_MODULE::GOTO(SERVO_GOTO *cmd_in)
{
// Put state table for SERVO_GOTO here.
const double INRANGE = 0.01;
if(STATE_MATCH(NEW_COMMAND))
{
commanded_position = cmd_in->position;
stateNext(S1);
}
else if(STATE_MATCH(S1,
fabs(commanded_position - actual_position) < INRANGE))
{
commanded_position = actual_position;
status = RCS_DONE;
stateNext(S2);
}
else if(STATE_MATCH(S2))
{
// idle
}
}
- Open up a DOS Command Prompt. Change directories to c:\rcs_ex. Run NMAKE to compile everything. You could also click the "Make" button to accomplish the same thing.
- If everything compiled, run rcs_ex.bat to start the controller and the Diagnostics Program. You will need to press a key 3 times since the batch file pauses after starting each part of the controller.( A common problem is not having java on the PATH or not having classes.zip in the CLASSPATH, you may want to check this if the diagnostics program doesn't come up.) The screen should look like this:
- Click the red checkbox that says "NOT CONNECTED" at the top. It should turn green and say connected 4 out of 4.
- This is the hierarchy screen. You can see the current command and status of every module from here. Hold down the mouse over the emove module. A list will appear. Select EMOVE_INIT and release the mouse. The modules should turn green for a fraction of a second, indicating they are executing. The outline of the modules will turn red for a second indicating they have a new command. (If you don't see these effects you may want to reduce the refresh time using the slider in the upper right.)
- Hold down the mouse over any of the modules and select HALT. That module and its subordinates should list a halt command as their current command.
- Select "Details" from the drop-down list in the upper-left that currently says "Hierarchy". The screen should look like this:
- Under "Commands Available" select EMOVE_GOTO. Under "Cmd To Send" select double position. Above the modify/clear buttons, enter 0.5 for the position and click "Modify". Click "Send". This will cause the emove module to start executing the goto command. Under "Current Status" you should see the position. Depending on your screen resolution you may need to click the "Down" button under this list to find the position variable. The position should increase over a couple of seconds to about 4.9E-1. It doesn't quite get to 0.5 because our servo level considers a difference of 0.01 to be in-range.
- Send a CHANGE_TOOL command to emove after setting the tool parameter to 99. The position should count back to zero and just after it reaches zero the value of tool should change to 99.
- Select the position variable under "Current Status". Check the "Plot this Status Variable" checkbox under the list. Send an EMOVE_GOTO message to emove again with a position of 0.5. Select "Graph" from the drop-down list in the upper-left that currently says "Details". Wait for the position to get almost to 0.5 and then click "Stop All Plotting". Then click "Fit to Graph". You should see the following graph:
- Congratulations!!! You have built and tested an RCS controller.