An informal document prepared in 1991 for a program design walkthrough. The contractor's software development methodology only required a structure chart and PDL, but I was getting tired of being invited to design reviews for programs whose function I had to deduce from reading the PDL. In addition, putting my thoughts down on paper helped me to map out my design.
CFGMON is the TPOCC configuration monitor, a program that periodically checks the values of a predefined set of telemetry parameters. The basic requirements for the configuration monitor, as set out in the SAMPEX and WIND/POLAR SRSs, are as follows:
The following pages provide a fairly high-level overview of the configuration monitor, after which you can dive right into the PDL. Among the topics discussed are:
State Manager <-----> <-----> Event Logger TSTOL <-----> cfgmon xcfgmon <-----> ^ <-----> Data Server | Shared Memory
CFGMON will support interfaces with:
[XQtag]" indicates an executable command. The optional tag is saved and used to tag responses returned to a client. The lack of a leading bracket expression implies "
[XQ]" with no tag.
Not shown on the diagram above are the disk files that CFGMON reads and writes:
A configuration file defines the parameters that CFGMON should monitor. Configuration files are normal UNIX text files containing a line for each parameter to be checked. Parameter names are specified in a TSTOL-style format:
The optional "
P@" prefix specifies that the EU-converted value
of the variable should be examined. A process name should be specified if
the parameter's mnemonic is not unique in the database. Values can be fetched
from remote systems by specifying the host name of the remote computer.
The remainder of a parameter's configuration entry specifies the tests that should be applied to the parameter's values:
parameter op operand [ and op operand ]
parameter op operand [ or op operand ]
The allowed operators include =, <>, <, <=, >, and >=. The optional AND- and OR-clauses provide for checking that a value falls in or out of a given range. For example, the following configuration entries for parameter P are interpreted as shown:
P >= X and <= Y
P < X or > Y
Operands can be one of three types:
When a parameter fails the tests specified in a configuration file, the event is logged to the event logger as well as being recorded in a configuration monitoring report. A separate report file is generated for each configuration monitored. If a configuration is monitored for more than one satellite pass, then the reports for subsequent passes are appended to the first report file.
The proposed format of the configuration monitoring report looks somewhat as follows:
Configuration: fileName Start of Pass: YY:DDD:HH:MM.SS Date/Time of Check Parameter Value Check Expression --------- --------- ----- ---------------- YY-DDD-HH:MM:SS P@AD-RAMP 123.456 ((P@AD-RAMP > 250) and (P@AD-RAMP < 300.5)) YY-DDD-HH:MM:SS BATT_VOLT Unchecked ... End of Pass: YY:DDD:HH:MM.SS
This is not the same as the format presented in the WIND/POLAR CDR, but it looks more readable, will be easier to generate, and will consume less paper.
Environment variable, $mission_CFG_FILE, like TSTOL's procedure file variable, specifies a list of pathnames used to find configuration files. For example:
% setenv mission_CFG_FILE "./.cfg, $PROJECT_DIR/runtime/config/"
The file name for a monitoring report is the configuration name, converted to lower-case. Environment variable, $mission_CFG_REPORT, provides default components for report file names. For example:
% setenv mission_CFG_REPORT "$PROJECT_DIR/runtime/config/.rpt"
Additional TPOCC environment variables:
Network server names:
CFGMON comes up, creates its listening sockets (mission_cfgmon and mission_cfgmon_ds), and sits around waiting for connection requests. When a state manager-like client calls, CFGMON answers the connection request and adds the client to its list of clients. When and if the connection is broken, the client and its configurations are deleted.
CFGMON recognizes the following commands received from a client:
The MONITOR command causes CFGMON to load the specified configuration file, build an expression list, and add the configuration to CFGMON's list of configurations. A configuration has the following attributes: the name of its configuration file, the expression list for the configuration, the monitoring interval, and a reference to the client who requested the configuration.
The configurations in CFGMON's configuration list are not actually monitored until the satellite pass begins. This is signalled to CFGMON by a PASS INIT command received from any of its clients. A PASS TERM command signals the end of a pass, at which time CFGMON ceases to monitor the configurations in its list.
The scheduling of configuration evaluations is accomplished using the timeout
capability of the system
select(2) call. Implementing the
scheduling in this manner avoids the need to use timers and interrupt handlers,
the code for which requires significant amounts of conditional compilation to
work under both the UNIX and VxWorks operating systems. The scheduling scheme
used by CFGMON, described in the following paragraphs, is sufficient for the
non-critical timing of configuration monitoring.
Each configuration has associated with it the time of the next evaluation,
which is initially the current time. Prior to calling
CFGMON queries its configurations to determine the time of the next evaluation.
The difference between this time and the current time is the timeout value
select(2). When the
returns, CFGMON first checks for and processes any connection requests or
incoming commands. It then scans the list of configurations and evaluates
any configurations whose interval has elapsed. After evaluation, a
configuration's time of next evaluation is incremented to:
current_time + requested_interval
Upon completing the scan of the configuration list, CFGMON determines the time
of the earliest upcoming evaluation and calls
select(2) returns because of an interrupt (ERRNO = EINTR),
CFGMON can simply recompute the timeout and loop to call
A "configuration" is simply a list of expressions that must be evaluated periodically, where the expressions are comparisons of actual telemetry values against their expected values. In CFGMON, an expression is represented by a tree of expression nodes, where an expression node is a reference to a system variable, a literal value, or an operator. For example, a configuration file entry of
SC_ATT < 45 or > 90
is represented by the following expression tree:
or / \ < > / \ / \ SC_ATT 45 SC_ATT 90
The evaluation of an expression utilizes a depth-first, recursive algorithm that evaluates each expression node in the tree, from the bottom up and from left to right. The evaluation of an expression node returns a value. A reference to a system variable returns its value retrieved out of shared memory or from a remote data server; a literal value returns itself. A relational operator evaluates each of its operands, compares them, and returns a true or false value. Logical operators (and and or) work in a similar fashion.
If the evaluation of an expression returns a final value of true, then the parameter passed its test. If false is returned, the parameter failed its configuration check. In that case, CFGMON logs an event message and records the failure in the report file.
(The preceding provides a basic understanding of how expressions are
represented and evaluated in the configuration monitor. The actual
implementation has 3 items for every parameter to be checked: information
about the parameter, its current value, and its test expression. Information
about the parameter [e.g., its mnemonic] is needed when logging configuration
failures. The current value of the parameter is fetched before evaluating its
test expression; it provides a single value for the multiple references in the
test expression, as well as saving the parameter's value for reporting
purposes. Finally, the test expression is as shown above, except that system
variable references [e.g., "
SC_ATT"] are linked to the parameter's
On first glance, the scheme above might seem overly elaborate. Wouldn't it be easier to just store the parameter, its 2 comparison operators, and its 2 operands in a simple record structure? However, the need to handle different types of telemetry data (integer, real, etc.) would necessitate pointers to other structures anyway. The small number of parameters involved (50 at most) and the low monitoring frequency (once every 10 seconds) make any performance penalty relatively insignificant. A benefit of this representation scheme is the ease with which CFGMON could be expanded to handle more complex configuration tests in the future.
CFGMON was designed in an object-oriented manner. Doing so broke the program up into small, functionally-independent modules. Once an understanding of CFGMON's purpose was achieved, the basic objects in the system were fairly obvious. The root object of the program is the configuration monitor itself. When created, the CFGMON object creates a system variable database object, an event logger object, an empty client list, an empty configuration list, etc.
The CFGMON object then sits in a loop, listening for connection requests from new clients and commands from existing clients. When a new client requests a connection, CFGMON creates a client object and adds it to the client list. When input is available on a network connection, CFGMON notifies the corresponding client object; the client object reads the message and sends it to the CFGMON object for interpretation.
When a MONITOR (CFGMON) directive is received, CFGMON creates a configuration object and adds it to the configuration list. When a PASS INIT or PASS TERM directive is received, the satellite pass object is notified. During a pass, an evaluate "message" is periodically sent to each of the configuration objects. Each configuration object, if ready, tests the parameters specified in its configuration file. If the test of a parameter fails, messages are sent to the event logger and report objects.
The objects in the CFGMON program are described in more detail further on. The program is implemented in an object-based fashion in C. A "message" is passed between objects via a function call to the selected "method" in the target object. For each type of object, there is a header file and a function file:
The header file defines the data structure for instances of the object and provides external definitions for functions that operate on these objects. The function file contains the source code for the functions; the first argument to each function is the object upon which the function operates. The functions belonging to a particular object class are stored in a single source file for two reasons:
The object-based technique utilized in CFGMON is similar to the X Toolkit technique for simulating object-oriented programming in C, but it differs in some respects:
objectP.h", to hide the representation of its objects. This is purely cosmetic, since nothing prevents the application programmer from including the private header file in his/her code.
This diagram illustrates the relationships between objects in CFGMON. Note that the diagram shows the logical relationships between instances of objects, not between classes of objects. For example, an expression list contains expressions. An expression, in turn, contains system variables and values (and operators).
Configuration Monitor Client List Client Message Configuration List Client Configuration Expression List Expression System Variable Value ... Report ... Satellite Pass System Variable Database Event Logger
The types of objects in the CFGMON program are described in this section. For each object type, the following information is given:
An object's methods are mapped to functions in the object's C source file using the following naming scheme:
For the sake of simplicity, not all methods or objects are explicitly implemented; these methods and objects are marked by asterisks (*). The exceptions are as follows:
create()function is just the main routine of the CFGMON program.
evt_init()is used in place of the event logger object's
vsend_event()is substituted for the logger object's
create()- Performs program initialization and listens for connection requests and commands.
parse()- Parses and processes commands received from clients.
destroy()- Terminates program.
create()- Answers connection request and creates XDR stream for client.
receive()- Receives message from client.
send()- Sends message to client.
socket()- Returns client's socket number.
destroy()- Closes connection and destroys XDR stream.
create()- Creates an empty client list.
add()- Adds a client object to the list of clients.
get()- Returns I-th client in the list.
length()- Returns number of clients in list.
mask()- Returns SELECT(2) mask for clients.
create()- Returns a tree whose root may be a literal value or a system variable reference, a unary operator applied to a single subtree, or a binary operator applied to 2 subtrees. Variable and value nodes reference "Value" objects. (Variable-length argument list)
evaluate()- Evaluates expression and returns "Value" object.
display()- Returns string representation of expression.
value()- Returns previously-evaluated value of expression.
create()- Creates an empty expression list.
add()- Adds an expression to the list of expressions.
get()- Returns I-th expression in the list.
length()- Returns number of elements in list.
create()- Load file and construct expression list.
evaluate()- Evaluates each expression in the list of expressions and reports failures.
next_update()- Returns time of next evaluation.
destroy()- Delete configuration.
create()- Initialize empty list.
add()- Create configuration and add to list. Other parameters: interval, client.
evaluate()- Scan list and evaluate configurations whose interval has elapsed.
next_update()- Scan list and determine earliest time of next evaluation.
delete()- Delete configuration from list.
destroy()- Destroy configuration list.
create()- Load system variable database and determine if data server is remote or local.
destroy()- Unload system variable database.
create()- Parses mnemonic specification ("
[P@]mnemonic[#process][@host]"), creates a tag structure for the system variable, and looks up its address in shared memory.
display()- Returns name of system variable.
get_value()- Returns "Value" object.
clone()- Returns duplicate of value.
compare()- Must handle different data types.
data()- Returns pointer to data buffer.
display()- Returns string version of value.
tag_of()- Tag from state manager.
text_of()- Message text.
type_of()- [XQ] or [ST].
start_time()- Returns time of pass INIT.
end_time()- Returns time of pass TERM.
create()- Open file for appending.
open()- Write header for pass.
write()- Write configuration event to file.
close()- Write trailer for pass.
destroy()- Close file.
create()- Initialize interface with event logger.
log()- Send message to logger.
TOY_cfgmon.c main () TOY_cfgmon_parse () TOY_cfgmon_destroy () TOY_client.c TOY_client_create () TOY_client_receive () TOY_client_send () TOY_client_socket () TOY_client_destroy () TOY_expr.c TOY_expr_create () TOY_expr_evaluate () TOY_expr_display () TOY_expr_value () TOY_expr_destroy () TOY_config.c TOY_config_create () TOY_config_evaluate () TOY_config_next_update () TOY_config_destroy () TOY_sysvar.c TOY_sysvar_create () TOY_sysvar_display () TOY_sysvar_get () TOY_sysvar_set () TOY_sysvar_destroy () TOY_syvdb.c TOY_syvdb_create () TOY_syvdb_destroy () TOY_value.c TOY_value_create () TOY_value_clone () TOY_value_compare () TOY_value_data () TOY_value_display () TOY_value_destroy () TOY_message.c TOY_message_create () TOY_message_tag () TOY_message_text () TOY_message_type () TOY_message_destroy () TOY_pass.c TOY_pass_create () TOY_pass_init () TOY_pass_term () TOY_pass_start_time () TOY_pass_end_time () TOY_pass_in_progress () TOY_pass_destroy () TOY_report.c TOY_report_create () TOY_report_open () TOY_report_write () TOY_report_close () TOY_report_destroy ()
The following chart shows the calling hierarchy of the modules in the configuration monitor program:
TOY_cfgmon . TOY_syvdb_create . TOY_pass_create . TOY_client_socket . TOY_config_next_update . TOY_client_create . TOY_client_receive . . TOY_client_destroy . . . TOY_config_destroy . . . . TOY_sysvar_destroy . . . . TOY_expr_destroy . . . . . TOY_value_destroy . . . . . TOY_expr_destroy . . . . . TOY_sysvar_destroy . . . . TOY_report_destroy . . TOY_message_create . . TOY_cfgmon_parse . . . TOY_message_text . . . TOY_config_create . . . . TOY_config_load . . . . . TOY_sysvar_create . . . . . TOY_expr_create . . . . TOY_report_create . . . . TOY_pass_in_progress . . . . TOY_config_pass . . . . . TOY_pass_in_progress . . . . . TOY_report_open . . . . . . TOY_pass_start_time . . . . . TOY_report_close . . . . . . TOY_pass_end_time . . . TOY_config_destroy . . . TOY_pass_init . . . TOY_pass_term . . . TOY_config_pass . . . TOY_client_send . . . . TOY_message_type . . . . TOY_message_tag . . . . TOY_message_text . . . . TOY_client_destroy . . . TOY_message_create . . . TOY_message_destroy . TOY_config_destroy . TOY_client_destroy . TOY_pass_in_progress . TOY_config_evaluate . . TOY_expr_evaluate . . . TOY_expr_evaluate . . . TOY_value_create . . . TOY_value_compare . . . TOY_sysvar_get . . TOY_expr_value . . . TOY_value_clone . . . . TOY_value_create . . TOY_report_write . . . TOY_sysvar_display . . . TOY_value_display . . . TOY_expr_display . . . . TOY_expr_display . . . . TOY_value_display Exit Handler: TOY_cfgmon_destroy . TOY_config_destroy . TOY_client_destroy . TOY_pass_destroy . TOY_syvdb_destroy Unused (but expected to be of use): TOY_sysvar_set . TOY_value_display . TOY_value_destroy