Skip to main content
U.S. flag

An official website of the United States government

Official websites use .gov
A .gov website belongs to an official government organization in the United States.

Secure .gov websites use HTTPS
A lock ( ) or https:// means you’ve safely connected to the .gov website. Share sensitive information only on official, secure websites.

RCS Library Lower Level Utilities

RCS Library Lower Level Utilities

Introduction

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".

The RCS_TIMER Class.

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:

  1. Initialize the RCS_TIMER object with the cycle period.
  2. Call RCS_TIMER::wait() at the end of each cycle.

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):

  1. Create a function that takes a (void *) as an argument and returns an int.
  2. Initialize the RCS_TIMER object with a cycle period used only for diagnostics, the address of the function and a parameter for the function.
  3. Use RCS_TIMER::wait().

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(); } } 

Constructor:

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.

The wait 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.

The load Function

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.

The sync Function

void RCS_TIMER::sync();

Restart the wait interval now.

Other Time Functions.

The esleep Function

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.

The etime Function

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().

The RCS_SEMAPHORE Class.

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:

  1. Create or attach to the semaphore by initializing the RCS_SEMAPHORE object. You will need an id agreed on by the all the processes using the semaphore and you must specify which process is responsible for creating the semaphore.
  2. Surround accesses to the shared resource with RCS_SEMAPHORE::wait() and RCS_SEMAPHORE::post()

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(); } }  

Constructor:

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.

The wait Function

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.

The trywait Function

int RCS_SEMAPHORE::trywait();

If the semaphore is available take it. Returns 0 for success or -1 for failure.

The post Function

int RCS_SEMAPHORE::post();

Release the semaphore. Returns 0 for success or -1 for failure.

The getvalue Function

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.

The RCS_LINKED_LIST Class.

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:

  1. Initialize a linked list.
  2. Store some objects on the list.
  3. Perform some operations on the objects on the list.

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); } 

Constructor

RCS_LINKED_LIST::RCS_LINKED_LIST();

Initializes the linked list.

The store_at_head Function

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.

The store_at_tail Function

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.

The store_before_current_node Function

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.

The store_after_current_node Function

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.

The get_head Function

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.

The get_tail Function

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.

The get_next Function

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.

The get_last Function

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.

The delete_current_node Function

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.

The delete_node Function

void RCS_LINKED_LIST::delete_node(int id);

Delete the node with the associated id.

The set_list_sizing_mode Function

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:

  • DELETE_FROM_TAIL: Remove one node from the tail of the list to make room for the new node.
  • DELETE_FROM_HEAD: Remove one node from the head of the list to make room for the new node.
  • STOP_AT_MAX: Return -1 if an attempt is made to add a new node when the list is full.
  • NO_MAXIMUM_SIZE: Allow the list to grow until all available memory is used up.

Destructor

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.

RCS Print Functions.

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.

The rcs_print Function

int rcs_print(char *_fmt, . . .);

Prints a message using the _fmt format string and optional additional arguments using the printf conventions.

The rcs_print_error Function

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().)

The rcs_print_debug Function

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().)

The rcs_vprint Function.

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.

The rcs_puts Function

int rcs_puts(char *_str);

Prints the string _str and adds a new line character at the end following the puts convention.

The strip_control_characters Function

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.

The set_rcs_print_destination Function

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:

RCS_PRINT_TO_STDOUT
Print to stdout.
RCS_PRINT_TO_LOGGER
Currently prints to stdout, except under VxWorks where it uses the logMsg function, which is non-blocking.
RCS_PRINT_TO_STDERR
Print to stderr
RCS_PRINT_TO_NULL
Make all rcs_print functions return without doing anything.
RCS_PRINT_TO_LIST
Store all rcs_print messages in a linked list, so that later they can be displayed in a separate window, or used in some other way. The current list sizing mode defaults to a maximum size of 256 with excess nodes being deleted from the head.

The set_rcs_print_flag Function

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

The clear_rcs_print_flag Function

void clear_rcs_print_flag(long flag_to_clear);

Clears a flag set with set_rcs_print_flag.

The get_rcs_print_list Function

RCS_LINKED_LIST *get_rcs_print_list();

Returns the address of the linked list where messages may have been stored.

The clean_rcs_print_list Function

void clean_print_list();

Deletes the linked list where messages may have been stored.

The set_rcs_print_list_sizing_mode Function

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.

Windows Functions

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:

  1. Call create_rcs_print_window().

The create_rcs_print_window Function

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.

The remove_rcs_print_window Function

void remove_rcs_print_window();

Send the WM_DESTROY message to the rcs print window.

Internet Functions

The RCS library includes a simple inteface for reading text files over the internet which is similar to the C functions in stdio.h. These functions sit on top of either the library from the World Wide Web Consortium or the Internet API (Application Programmer`s Interface) in the ActiveX SDK (Software Developer's Kit) included with Microsoft Visual C++ Version 4.2 and later. Here are the functions:
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)

Created July 11, 2014, Updated January 6, 2017