The Real-Time Control Systems(RCS) library includes several utilities that aid portability of source code. The most significant utilities CMS/NML and the NODE class are described in separate documents. This guide will expose some of the lower level classes and functions which were used in constructing the higher level utilities but may also be useful on their own. To use the utilities link with the RCS library and include "rcs.hh" just as you would to use CMS/NML or the NODE class. Some utilities are not available under all of the platforms. See the "Platforms Supported" line under each major heading.
Most of the examples have corresponding text files which can be down loaded and compiled. (WWW Users: The examples are included both directly in this document for easy reading and as separate text-only files which are ready to be compiled.) Unfortunately, given the variety of systems and compilers that are available it is impossible for me to give detailed compiling instructions here. However the following form should work on most systems.(All typed on one line.)
[C++ Compiler] -I[Location of RCS Include Files] [Example C++ File(s)] [RCS Library for Platform] -o [Executable File]
For users of Sun4 computers on the NIST, ISD LAN the first example would be compiled like this.
g++ -I/home/manta/rcslib/plat/sunos4/include utilex1.cc /home/manta/rcslib/plat/sunos4/lib/librcs.a -o utilex1
Since a working knowledge of C++ will be very helpful for understanding or using the RCS library utilities you may want to review "A Quick C++ Introduction for RCS Library Users".
Platforms Supported: All
Programmers can create RCS_TIMER objects to synchronize to the system clock or to some other event(s).
To synchronize a cyclic process to the system clock:
The cycle period will be rounded up to the resolution of the system clock or the most precise time measuring or sleeping function available for the given platform. RCS_TIMER::wait() will wait the remainder of the cycle period since the last call. The units for the cycle time are seconds.
Example: utilex1.cc
#include "rcs.hh" main() { RCS_TIMER timer(0.02);/* Initialize timer for 20 millisecond cycles. */ while(1) { /* Do some processing. */ timer.wait(); /* Wait for the end of cycle. */ } }
To synchronize to some other event(s):
The user's function should return 0 when the event to synchronize to occurs or -1 if an error occurs. The argument passed to the users function will be whatever was passed as the third parameter to the constructor of the RCS_TIMER or NULL if no third argument is given. This argument could be used by the synchronizing function to know which timer is calling it if the synchronization function is called by more than one timer. Nothing will force the function to return within the cycle period but there are ways to check if the function took longer than the cycle period after it returns.
Example:utilex2.cc
#include "rcs.hh" #include int my_sync_func(void *arg) { getchar(); return(0); } main() { RCS_TIMER timer(0.02, my_sync_func, NULL); while(1) { /* Do some processing. */ timer.wait(); } }
RCS_TIMER::RCS_TIMER(double _interval, RCS_TIMERFUNC _function = NULL, void *_arg = NULL);
Initialize a new RCS_TIMER object. _interval is the cycle period. _function is an optional function for synchronizing to an event other than the system clock. _arg is a parameter that will be passed to function.
int RCS_TIMER::wait();
Wait until the end of interval or until a user function returns.
Returns:
0 for success, the number of cycles missed if it missed some cycles, or
-1 if some other error occurred.
double RCS_TIMER::load();
Returns the percentage of loading by the cyclic process. If the process spends all of its time waiting for the synchronizing event then it returns 0.0. If it spends all of its time doing something else before calling wait then it returns 1.0. The load percentage is the average load over all of the previous cycles.
void RCS_TIMER::sync();
Restart the wait interval now.
Platforms Supported: All
void esleep(double _secs);
Go to sleep for _secs seconds. The time will be rounded up to the resolution of the system clock or the most precise sleep or delay function available for the given platform.
double etime();
Return the number of seconds from some event. The time will be rounded up to the resolution of the system clock or the most precise time measuring function available for the given platform. For the value returned to mean anything you need to be able to compare it with a value stored from a previous call to etime().
Platforms Supported: All except DOS and Windows.
RCS_SEMAPHORE objects can be used for mutual exclusion of resources shared by multiple processes on the same host.
To use:
Example: utilex3a.cc, utilex3b.cc, utilex3.hh
utilex3a.hh
/* utilex3.hh */ #ifndef UTILEX3_HH #define UTILEX3_HH #ifdef __cplusplus extern "C" { #endif #include /* defines the S_IXXXX permission flags */ #ifdef __cplusplus } /* END of extern "C" */ #endif /* ID that processes connecting to this semaphore must agree on. */ #define MY_SEM_ID 101 /* Permissions Mode for rw_rw_r__ */ #define MY_SEM_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) #endif/* end of UTILEX3_HH */
utilex3a.cc
/* utilex3a.cc */ #include "utilex3.hh" #include "rcs.hh" /* Process A */ main() { RCS_SEMAPHORE my_sem(MY_SEM_ID, /* Both processes must agree on id. */ RCS_SEMAPHORE_CREATE, /* Create the semaphore. */ 0.100, /* timeout = 100 milliseconds */ MY_SEM_MODE, /* Set permissions for semaphore */ 1); /* Initial State */ if(-1 != my_sem.wait()) { /* Access shared resource. */ my_sem.post(); } }
utilex3b.cc
/* utilex3b.cc */ #include "utilex3.hh" #include "rcs.hh" /* Process B */ main() { RCS_SEMAPHORE my_sem(MY_SEM_ID, /* Both processes must agree on id. */ RCS_SEMAPHORE_NOCREATE, /* Do not Create the semaphore. */ 0.100); /* timeout = 100 milliseconds */ if(-1 != my_sem.wait()) { /* Access shared resource. */ my_sem.post(); } }
RCS_SEMAPHORE::RCS_SEMAPHORE(unsigned long int _id, int _oflag, double _timeout, int _mode = DEFAULT_SEM_MODE, int _state = 0);
Initializes an RCS_SEMAPHORE object. If _oflag equals RCS_SEMAPHORE_CREATE a semaphore is created. If _oflag equals RCS_SEMAPHORE_NOCREATE the process will try to attach to a semaphore that must already have been created with the same _id. If _timeout is positive, then calls to RCS_SEMAPHORE::wait() will return -1 after timeout seconds. If _timeout is negative, then RCS_SEMAPHORE::wait() will wait indefinitely for the semaphore to be available. If _timeout is zero, then RCS_SEMAPHORE::wait() will return immediately with 0 if the semaphore was available or -1 if it was not. The _mode determines which users will have permission to use the semaphore. You can or together symbolic constants from sys/stat.h. The default value of _mode, DEFAULT_SEM_MODE, allows read and write access to everyone. The _mode will be ignored if the process is not creating the semaphore. The _state should be 1 to make the semaphore immediately available. The _state will be ignored if the process is not creating the semaphore.
int RCS_SEMAPHORE::wait();
Wait for the semaphore to be available and then take it. See the constructors parameters for several options affecting its behavior. Returns 0 for success or -1 for failure.
int RCS_SEMAPHORE::trywait();
If the semaphore is available take it. Returns 0 for success or -1 for failure.
int RCS_SEMAPHORE::post();
Release the semaphore. Returns 0 for success or -1 for failure.
int RCS_SEMAPHORE::getvalue();
Test to see if the semaphore is available but don't take it even if it is. Returns a positive integer if the semaphore is available or 0 if it is not.
Platforms Supported: All.
RCS_LINKED_LIST objects can be used for a variety of linked list applications. It includes an internal pointer that keeps track of the current node.
To use:
Example: utilex4.cc
#include "rcs.hh" class MY_STRUCT { public: int count; }; RCS_LINKED_LIST *my_list; int compute_total_count(RCS_LINKED_LIST *my_list); main() { int totalCount; MY_STRUCT S1, S2, S3; my_list = new RCS_LINKED_LIST(); /* Initialize and create linked list. */ /* Store S1 on the end of list. */ my_list->store_at_tail(&S1, sizeof(MY_STRUCT), 0); /* Store S2 on the end of list. */ my_list->store_at_tail(&S2, sizeof(MY_STRUCT), 0); /* Store S3 on the end of list. */ my_list->store_at_tail(&S3, sizeof(MY_STRUCT), 0); totalCount = compute_total_count(my_list); } int compute_total_count(RCS_LINKED_LIST *my_list) { MY_STRUCT *ptr_to_struct; int total_count = 0; /* Get first object and initialize the internal pointer to start of list. */ ptr_to_struct = (MY_STRUCT *) my_list->get_head(); while(NULL != ptr_to_struct) { total_count += ptr_to_struct->count; ptr_to_struct = (MY_STRUCT *) my_list->get_next(); } return(total_count); }
RCS_LINKED_LIST::RCS_LINKED_LIST();
Initializes the linked list.
int RCS_LINKED_LIST::store_at_head(void *_data, size_t _size, int _copy);
Creates a new node and places it at the beginning of the list. If _copy is nonzero then this function will malloc _size bytes and copy _size bytes from the address starting at _data there and the get functions will return a pointer to the copy of the object. If _copy is zero then the _data pointer will be stored and the get functions will return a pointer to the original object.
Returns a positive integer id that can be used to select this node later if successful or -1 if an error occurred.
int RCS_LINKED_LIST::store_at_tail(void *_data, size_t _size, int _copy);
Creates a new node and places it at the end of the list. If _copy is nonzero then this function will malloc _size bytes and copy _size bytes from the address starting at _data there and the get functions will return a pointer to the copy of the object. If _copy is zero then the _data pointer will be stored and the get functions will return a pointer to the original object.
Returns a positive integer id that can be used to select this node later if successful or -1 if an error occurred.
int RCS_LINKED_LIST::store_before_current_node(void *_data, size_t _size, int _copy);
Creates a new node and places it before the current node. If _copy is nonzero then this function will malloc _size bytes and copy _size bytes from the address starting at _data there and the get functions will return a pointer to the copy of the object. If _copy is zero then the _data pointer will be stored and the get functions will return a pointer to the original object.
Returns a positive integer id that can be used to select this node later if successful or -1 if an error occurred.
int RCS_LINKED_LIST::store_after_current_node(void *_data, size_t _size, int _copy);
Creates a new node and places it after the current node. If _copy is nonzero then this function will malloc _size bytes and copy _size bytes from the address starting at _data there and the get functions will return a pointer to the copy of the object. If _copy is zero then the _data pointer will be stored and the get functions will return a pointer to the original object.
Returns a positive integer id that can be used to select this node later if successful or -1 if an error occurred.
void *RCS_LINKED_LIST::get_head();
Get the address of the first object on the list and set the current node to the beginning of the list.
If the list is empty get_head returns null. Depending on how the object was stored the address this function returns may be the address of the original object or of a copy.
void *RCS_LINKED_LIST::get_tail();
Get the address of the object at the end of the list and set the current node to the end of the list. If the list is empty get_tail returns null. Depending on how the object was stored the address this function returns may be the address of the original object or of a copy.
void *RCS_LINKED_LIST::get_next();
Get the address of the next object on the list and move the current node one step closer to the tail.. If the list is empty get_tail returns null. Depending on how the object was stored the address this function returns may be the address of the original object or of a copy.
void *RCS_LINKED_LIST::get_last();
Get the address of the previous object on the list and move the current node one step closer to the head.. If the list is empty get_tail returns null. Depending on how the object was stored the address this function returns may be the address of the original object or of a copy.
void RCS_LINKED_LIST::delete_current_node();
Remove the current node from the list and free any memory associated with it. Some extra pointers keep track of the node that was before and after the deleted node so that the next call to get_next or get_last will return the same object as if the current node was not deleted.
void RCS_LINKED_LIST::delete_node(int id);
Delete the node with the associated id.
void RCS_LINKED_LIST::set_list_sizing_mode(int _maximum_size, LIST_SIZING_MODE _new_mode);
Sets a sizing mode and the maximum number of nodes allowed on the list. The sizing mode determines what happens when there is an attempt to add another node to the list after it has reached the _maximum_size. The following are the possible values for _new_mode:
RCS_LINKED_LIST::~RCS_LINKED_LIST();
Removes every node from the list and frees all the memory associated with the nodes or the list itself.
Platforms Supported: All
Although ANSI C provides input/output facilities such as printf these functions may not be appropriate under certain circumstances. For example, under Windows unless you compile with the EasyWin(Borland C++) or QuickWin(Visual C++) option there will be no window created to catch printf messages, and there are some limitations if you choose the EasyWin or QuickWin options. The RCS print functions work much like the ANSI C facilities but can more easily redirect their output. Controlling where the output of the RCS print functions goes also controls the error messages of CMS/NML and the debug messages of the NODE class.
Example: roots.cc, roots.hh, utilex5u.cc, utilex5w.cc
roots.hh
/* roots.hh */ #ifndef ROOTS_HH #define ROOTS_HH /* Function for computing the roots of a quadratic. */ void compute_roots( double A, double B, double C, double &root1, double &root2); #endif /* end of ROOTS_HH */
roots.cc
#include "roots.hh" #include "rcs.hh" #include /* Function for computing the roots of a quadratic. */ void compute_roots( double A, double B, double C, double &root1, double &root2) { if(B*B-4*A*C < 0) { rcs_print("compute_roots: Can't compute square root of %lf\n", B*B-4*A*C); return; } root1 = (-B+sqrt(B*B-4*A*C))/(2*A); root2 = (-B-sqrt(B*B-4*A*C))/(2*A); }
utilex5u.cc
#include "rcs.hh" #include "roots.hh" /* A UNIX or DOS Application which uses compute_roots. */ main() { double r1, r2; /* Send all the rcs_print messages to the standard output. */ set_rcs_print_destination(RCS_PRINT_TO_STDOUT); /* Try to compute the roots of a quadratic that will cause an error.*/ compute_roots(1, 1, 1, r1, r2); }
utilex5w.cpp
#include "rcs.hh" #include "roots.hh" #include /* A Windows Application which uses compute_roots. */ int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) { double r1, r2; MSG msg; /* Store all the rcs_print messages in a linked list that a window can display later. */ set_rcs_print_destination(RCS_PRINT_TO_LIST); /* Create a window to show rcs_print messages, and display it as an icon to start. It has no parent window. */ create_rcs_print_window(hInstance, SW_MINIMIZE, NULL); /* Compute the roots of a quadratic that will produce an error. */ compute_roots(1, 1, 1, r1, r2); /* Wait for someone to kill this process. */ while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return(msg.wParam); }
The Windows Application also uses the create_rcs_print_window function described under Windows Functions.
int rcs_print(char *_fmt, . . .);
Prints a message using the _fmt format string and optional additional arguments using the printf conventions.
int rcs_print_error(char *_fmt, . . .);
Prints a message using the _fmt format string and optional additional arguments using the printf conventions if the PRINT_RCS_ERRORS flag is set. (See set_rcs_print_flag().)
int rcs_print_debug(long _flag_to_check, char *_fmt, . . .);
.Prints a message using the _fmt format string and optional additional arguments using the printf conventions if the corresponding flag to _flag_to_check is set. (See set_rcs_print_flag().)
int rcs_vprint(char *_fmt, va_list _va_args);
Prints a message using the _fmt format string and the _va_args using the vprintf conventions.
int rcs_puts(char *_str);
Prints the string _str and adds a new line character at the end following the puts convention.
char *strip_control_characters(char *_dest, char *_src);
Removes new lines, carriage returns and tabs from the _src string and stores the result in the _dest string if the _dest pointer does not equal NULL. If the _dest pointer equals NULL the new string is stored in an internal array.
Returns the dest pointer or the address of the internal array where the new string was stored.
void set_rcs_print_destination(RCS_PRINT_DESTINATION_TYPE _type);
Changes where the output of the rcs_print functions is directed. The following choices are available:
void set_rcs_print_flag(long flag_to_set);
An internal 32 bit integer contains a set of flags that are checked whenever an rcs_print_debug or rcs_print_error occurs to determine if the message should be printed or not. Programmers can define their own flags in the most significant byte or turn on or off several NODE or CMS/NML debug messages.
The current messages that can be turned on or off are:
/* Print MODE flags. */
PRINT_RCS_ERRORS /* ON by default.*/
PRINT_NODE_CONSTRUCTORS
PRINT_NODE_DESTRUCTORS
PRINT_CMS_CONSTRUCTORS
PRINT_CMS_DESTRUCTORS
PRINT_NML_CONSTRUCTORS
PRINT_NML_DESTRUCTORS
PRINT_COMMANDS_RECIEVED
PRINT_COMMANDS_SENT
PRINT_STATUS_RECIEVED
PRINT_STATUS_SENT
PRINT_NODE_CYCLES
PRINT_NODE_MISSED_CYCLES
PRINT_NODE_CYCLE_TIMES
PRINT_NODE_PROCESS_TIMES
PRINT_NEW_WM
PRINT_NODE_ABORT
void clear_rcs_print_flag(long flag_to_clear);
Clears a flag set with set_rcs_print_flag.
RCS_LINKED_LIST *get_rcs_print_list();
Returns the address of the linked list where messages may have been stored.
void clean_print_list();
Deletes the linked list where messages may have been stored.
void set_rcs_print_list_sizing_mode(int max_size, LIST_SIZING_MODE mode);
The print list will have a default maximum size of 256 and it will delete nodes from the head of the list to make room for new nodes. To change these settings it is preferable to use this function rather than getting the print list and using RCS_LINKED_LIST::set_list_sizing_mode() since it works regardless of whether the print list has been initialized yet.
Platforms Supported: MS-Windows 3.11, Windows 95, and Windows NT with Borland C++ or Microsoft Visual C++
After the messages from rcs_print have been stored in the linked list, Windows programs can go through the list themselves or create a special window to display these messages automatically.
To create a window for the rcs_print messages:
HWND create_rcs_print_window(HANDLE hInstance, int nCmdShow, HWND hwndParent);
Creates a window to display rcs_print messages. It will be updated automatically when the rcs_print function is called or when one of its windows controls are used. The hInstance parameter should equal the first parameter passed to WinMain. The nCmdShow will be passed to ShowWindow. See the Windows API for ShowWindows options. The two most common values of nCmdShow are SW_MINIMIZE to start the window as an icon or SW_SHOWNORMAL to open the window immediately. You can create the window as a child of hwndParent or set hwndParent to NULL to make the window independent.
void remove_rcs_print_window();
Send the WM_DESTROY message to the rcs print window.
int inet_file_init(char *agent_name, char *version, int debug); INET_FILE *inet_file_open(char *url, char *type); char *inet_file_gets(char *buf, int maxlen, INET_FILE *fp); int inet_file_eof(INET_FILE *fp); int inet_file_close(INET_FILE *fp); int inet_file_exit();inet_file_init can be called before any calls to the other functions to set the agent_name, and version and to turn on debugging. The agent_name and version string are supplied to any HTTP servers you connect to mostly just for logging statistics about which browser is more popular. If debug is nonzero several diagnostics messages will be printed to stdout. If you never call inet_file_init it will be called with the first inet_file_open with a default agent_name, and version string and debugging off. The url passed to inet_file_open can be any valid URL or file name on your local system. For local filenames it is NOT necessary to use "file:\\" at the beginning. The type should always be "r" for read-only. inet_file_gets stores one line up to maxlen bytes in the buffer buf. inet_file_eof returns zero if there is more of the file to read and nonzero otherwise. inet_file_close should be used to close each INET_FILE handle and inet_file_exit should be called only just before the application exits to free up some memory/resources.
Last Modified: 03/23/00
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 (shackle[at]cme[dot]nist[dot]gov)