/*--------------------------------------------------------------------
* File: gtksimple.g
*
* Description: Cogent Tools Demo Tutorial One for GTK.
*
* Functions:
* start_qnserves
* program_startup
* reset_value
* write_data
* set_label
* table_labeler
* toggle_sym
* make_scale
* make_prog_bar
* make_spinner
* change_settings
* create_monitor
* main
*------------------------------------------------------------------*/
/********************************************************
* STARTING UP *
********************************************************/
/*--------------------------------------------------------------------
* Function: start_qnserves
* Returns: t on success, or nil
* Description: Used to start qserve or nserve, as specified. Called by
* the program_startup() function.
*------------------------------------------------------------------*/
function start_qnserves(command, name)
{
for(i=0; i<=50; i++)
{
if ((_os_ == "Linux") || (_os_ == "QNX4"))
if ((qnx_name_locate(0, name, 0) == nil))
{
system(command);
usleep(10000);
}
else
{
i = 50;
nil;
}
else
if (access(string("/dev/", command), 0) == -1)
{
system(command);
usleep(10000);
}
else
{
i = 50;
nil;
}
}
}
/*--------------------------------------------------------------------
* Function: program_startup
* Returns: t or nil
* Description: Starts any necessary Cogent software and the main window.
* Called by the main() function.
*------------------------------------------------------------------*/
function program_startup (proc_name, queue_name, win_fn)
{
local window, tsk;
/* See if qserve and nserve are running. If not, start them. */
start_qnserves("qserve", "qserve");
start_qnserves("nserve", "sc/nserve");
/* Start interprocess communication. */
if (init_ipc(proc_name, queue_name, "toolsdemo") == nil)
error("Could not initialize IPC.");
/* See if a DataHub is running in the toolsdemo domain.
If not, start it and a DataHub Viewer. */
if ((tsk = locate_task ("/dh/toolsdemo", t)) == nil)
system("datahub -d toolsdemo");
else
close_task (tsk);
if ((pid_dhview = fork()) == 0)
{
/* The child process is the DataHub Viewer. */
exec ("dhview", "-d", "toolsdemo");
/* In case the above exec commands fails, the following will be
* called, and it's virtually guaranteed not to fail. Thus in
* any circumstance a child process gets created.*/
exec("/bin/true");
}
princ("The process ID of the DataHub Viewer is: ", pid_dhview, "\n");
/* Make sure the PID Emulator is not already running. */
if ((tsk = locate_task ("emul", t)) != nil)
{
send(tsk, #exit_program(0));
close_task (tsk);
}
/* Start the PID Emulator. */
if ((pid_emul = fork()) == 0)
{
/* The child process is the PID Emulator. */
exec ("gamma", "emul");
exec("/bin/true");
}
princ("The process ID of the PID Emulator is: ", pid_emul, "\n");
/* Kill child processes when this program exits. */
atexit(`kill(pid_dhview, SIGINT));
atexit(`kill(pid_emul, SIGINT));
/* Create and register some points with the datahub. */
SP_001 = register_point (#SP_001);
PV_001 = register_point (#PV_001);
MV_001 = register_point (#MV_001);
AUTO_001 = register_point (#AUTO_001);
FREQ_001 = register_point (#FREQ_001);
PID1_Kp = register_point (#PID1_Kp);
PID1_Ki = register_point (#PID1_Ki);
PID1_Kd = register_point (#PID1_Kd);
PROP_001 = register_point (#PROP_001);
INT_001 = register_point (#INT_001);
/* Create the main window, and put in a destructor function for GTK */
window = eval(win_fn);
window.signal ("destroy", #exit_program(0));
}
/********************************************************
* THE MONITOR *
********************************************************/
TRUE = 1;
FALSE = 0;
/*--------------------------------------------------------------------
* Function: reset_value
* Returns: t or nil
* Description: Resets the value of a widget's adjustment.
*------------------------------------------------------------------*/
function reset_value(widget, newval, adj_id)
{
local newadj;
newadj = widget.get_adjustment();
if (adj_id)
{
gtk_signal_handler_block (newadj, adj_id);
newadj.set_value(100 - newval);
gtk_signal_handler_unblock (newadj, adj_id);
}
else
newadj.set_value(newval);
}
/*--------------------------------------------------------------------
* Function: write_data
* Returns: t or nil
* Description: Reads a widget that contains an adjustment value,
* and writes its value to the datahub.
*------------------------------------------------------------------*/
function write_data(widget, !ptname)
{
local ptval;
ptval = widget.get_adjustment();
write_point(string(ptname), ptval.value);
}
/*--------------------------------------------------------------------
* Function: set_label
* Returns: t or nil
* Description: Sets the label of a button according to
* the value of a database point.
*------------------------------------------------------------------*/
function set_label(button, sym, onvalue, offvalue)
{
if (eval(sym) != 0)
button.label = onvalue;
else
button.label = offvalue;
}
/*--------------------------------------------------------------------
* Function: table_labeler
* Returns: t or nil
* Description: Creates a label and puts it into a table.
*------------------------------------------------------------------*/
function table_labeler(table, txt, left, right, top, bottom)
{
local label;
label = new(GtkLabel);
label.set_text(txt);
table.attach_defaults(label, left, right, top, bottom);
}
/*--------------------------------------------------------------------
* Function: toggle_sym
* Returns: t or nil
* Description: Toggles a database point between 0 and 1 when a button
* is pressed, and calls set_label() to change the label
* accordingly.
*------------------------------------------------------------------*/
function toggle_sym(button, sym, onvalue, offvalue)
{
set (sym, eval(sym) == 0 ? 1 : 0);
write_point (sym, eval(sym));
set_label(button, eval(sym), onvalue, offvalue);
}
/*--------------------------------------------------------------------
* Function: make_scale
* Returns: A GtkVScale
* Description: Makes a scale based on adjustment parameters. Then makes
* a ruler and a label and then packs them together into a
* GtkVBox, and packs that box into a container.
*------------------------------------------------------------------*/
function make_scale(title, color, container, adjvar,
low, high, step, page)
{
local adj, adj2, newvalue, scale, label, scalebox;
/* Two adjustments are necessary right now since the
* GtkRange.set_inverted() doesn't exist yet, even in C code.
* We use the first adjusment to handle the actual value, and
* the second adjustment for the range and ruler, whose values
* are inverted from the first adjustment.
*/
adj1 = gtk_adjustment_new (0, 0, 100, step, page, 0.0);
adj2 = gtk_adjustment_new (0, 0, 100, step, page, 0.0);
newvalue = eval(adjvar);
if (number_p(newvalue))
{
adj1.value = newvalue;
adj2.value = 100 - newvalue;
}
scale = new(GtkVScale);
scale.set_adjustment(adj2);
scale.set_update_policy(GTK_UPDATE_CONTINUOUS);
scale.height = 200;
scale.set_draw_value(FALSE);
scale.set_color(1, (GTK_STATE_NORMAL &&
GTK_STATE_ACTIVE), color);
rule = new(GtkVRuler);
rule.set_range (100, 0, -1, 200);
rule.position = - 5;
rule.set_sensitive(FALSE);
label = new (GtkLabel);
label.set_text(title);
label_val = new(GtkLabel);
label_val.set_text(substr(string((eval(adjvar) * 100) / 100), 0, 6));
/* Implement the adjustments on the value label, ruler, and scale. */
adj2.signal("value_changed", `(@adj1).set_value(100 - (@adj2).value));
adj2.signal("value_changed",
`(@label_val).set_text(substr(string(((@adj1).value * 100) / 100),
0, 6)));
adj2.signal("value_changed", `(@rule).position = (100 - (@adj2).value));
adj2_id = adj2.signal("value_changed", `write_point(#SP_001, (@adj1).value));
add_exception_function(#SP_001, `reset_value(@scale, SP_001, @adj2_id));
/* Create a table to facilitate the width and spacing of the widgets. */
table = gtk_table_new(1, 5, FALSE);
table.border_width = 5;
table.set_homogeneous(TRUE);
table.attach_defaults(scale, 1, 2, 0, 1);
table.attach_defaults(rule, 2, 4, 0, 1);
scalebox = new(GtkVBox);
scalebox.pack_start(label, TRUE, TRUE, 0);
scalebox.pack_start(table, TRUE, TRUE, 0);
scalebox.pack_start(label_val, TRUE, TRUE, 0);
container.pack_start(scalebox, TRUE, TRUE, 0);
scale.show();
scale;
}
/*--------------------------------------------------------------------
* Function: make_prog_bar
* Returns: t or nil
* Description: Makes a progress bar based on adjustment parameters.
* It puts the progress bar into a table to facilitate
* proper layout. It also makes a label and packs the
* bar and label together into a container.
*------------------------------------------------------------------*/
function make_prog_bar (title, container, adjvar, color)
{
local label, table, adj, pbar;
label = new(GtkLabel);
label.set_text(title);
container.pack_start(label, TRUE, TRUE, 0);
table = gtk_table_new(1, 5, FALSE);
table.border_width = 5;
table.set_homogeneous(TRUE);
container.pack_start(table, TRUE, TRUE, 0);
adj = gtk_adjustment_new (0, 0, 100, 0, 0, 0.0);
adj.set_value(eval(adjvar));
pbar = new(GtkProgressBar);
pbar.adjustment = adj;
pbar.set_format_string("%v");
pbar.set_orientation(GTK_PROGRESS_BOTTOM_TO_TOP);
pbar.height = 200;
pbar.set_color (1, 2, color);
pbar.set_show_text(1);
add_exception_function(adjvar, `(@pbar).adjustment.set_value(@adjvar));
table.attach_defaults(pbar, 1, 4, 0, 1);
}
/*--------------------------------------------------------------------
* Function: make_spinner
* Returns: A GtkSpinButton
* Description: Makes a spin button based on adjustment parameters,
* and packs it into a table.
*------------------------------------------------------------------*/
function make_spinner(adjvar, digits, low, high, step, page,
table, left, right, top, bottom)
{
local adj, newvalue, spinner;
adj = gtk_adjustment_new (0, low, high, step, page, 0.0);
newvalue = eval(adjvar);
if (number_p(newvalue))
adj.value = newvalue;
spinner = new(GtkSpinButton);
spinner.set_adjustment(adj);
spinner.set_update_policy(GTK_UPDATE_CONTINUOUS);
spinner.set_digits(digits);
spinner.width = 40;
spinner.set_value(.05);
adj.signal("value_changed", `write_data(@spinner, @adjvar));
spinner.show();
add_exception_function(adjvar, `reset_value(@spinner, @adjvar, nil));
table.attach_defaults(spinner, left, right, top, bottom);
spinner;
}
/*--------------------------------------------------------------------
* Function: change_settings
* Returns: t or nil
* Description: Changes PID settings in the Monitor, and optionally
* unclicks the Auto button if it is on.
* Note: In the Demo, this function is a Library function, found
* in lib/linux.g.
*------------------------------------------------------------------*/
function change_settings(button, auto, sp1, v1, sp2, v2, sp3, v3, sp4,
v4, sp5, v5, sp6, v6, msgno, autobutton?)
{
if (button.get_active() == TRUE)
{
if (auto == nil)
{
if (AUTO_001 != 0)
autobutton.clicked();
}
else
sp1.set_value(v1);
sp2.set_value(v2);
sp3.set_value(v3);
sp4.set_value(v4);
sp5.set_value(v5);
sp6.set_value(v6);
}
}
/*--------------------------------------------------------------------
* Function: create_monitor
* Returns: A GtkWindow
* Description: Creates the Monitor window.
*------------------------------------------------------------------*/
function create_monitor ()
{
local win_monitor, main_title, scale, button, label, table;
local ambutton, frame, box1, box2, box3, onstring, offstring;
local s1, s2, s3, s4, s5, s6, but1, but2, but3, but4;
win_monitor = new (GtkWindow);
win_monitor.signal ("destroy", #win_monitor = nil);
win_monitor.set_color(1, GTK_STATE_NORMAL, 0xdddddd);
win_monitor.title = "Cogent Tools Demo: Monitor";
win_monitor.border_width = 5;
main_title = new (GtkLabel);
main_title.set_text("Cogent Tools Demo - Monitor");
main_title.height = 25;
frame = new(GtkFrame);
frame.set_color(1, GTK_STATE_NORMAL, 0xff0000);
frame.border_width = 5;
frame.add(main_title);
box1 = new (GtkHBox);
win_monitor.add (box1);
box2 = new (GtkVBox);
box2.spacing = 5;
box2.border_width = 5;
box2.pack_start(frame, TRUE, TRUE, 5);
box1.pack_start (box2, TRUE, TRUE, 0);
box2.show();
box3 = new(GtkHBox);
box2.pack_start(box3, TRUE, TRUE, 0);
/********************************************************
* Set point Panel *
********************************************************/
box4 = new(GtkVBox);
box4.set_border_width(5);
evaluation = eval(SP_001);
scale = make_scale("SP:\nSet point", 0x9da3eb,
box4, #SP_001, 0.0, 100.0, 0.1, 5.0);
table = gtk_table_new(2, 2, FALSE);
table.set_row_spacings(5);
table_labeler(table, "Auto mode ", 0, 1, 0, 1);
ambutton = new(GtkButton);
set_label(ambutton, AUTO_001, "ON", "OFF");
ambutton.signal("clicked",
`toggle_sym(@ambutton, #AUTO_001, "ON", "OFF"));
add_exception_function(#AUTO_001,
`set_label(@ambutton, #AUTO_001,"ON", "OFF"));
table.attach_defaults(ambutton, 1, 2, 0, 1);
table_labeler(table, "Delay", 0, 1, 1, 2);
s1 = make_spinner(#FREQ_001, 0, 1.0, 20.0, 1.0, 1.0,
table, 1, 2, 1, 2);
box4.pack_start(table, TRUE, TRUE, 0);
frame = new(GtkFrame);
frame.set_color(1, GTK_STATE_NORMAL, 0xaaaaff);
frame.set_shadow_type(GTK_SHADOW_IN);
frame.add(box4);
box3.pack_start(frame, TRUE, TRUE, 5);
/********************************************************
* Control output Panel *
********************************************************/
box4 = new(GtkVBox);
box4.set_border_width(5);
make_prog_bar("MV:\nControl Output", box4, #MV_001, 0x41be41);
table = gtk_table_new(2, 3, FALSE);
table.set_row_spacings(5);
table_labeler(table, "Kp: Prop", 0, 1, 0, 1);
PID1_Kp = register_point (#PID1_Kp);
s2 = make_spinner(#PID1_Kp, 2, 0.0, 1.0, 0.01, 0.1,
table, 1, 2, 0, 1);
table_labeler(table, " Ki: Int", 0, 1, 1, 2);
PID1_Ki = register_point (#PID1_Ki);
s3 = make_spinner(#PID1_Ki, 2, 0.0, 5.0, 0.01, 0.1,
table, 1, 2, 1, 2);
table_labeler(table, "Kd: Deriv", 0, 1, 2, 3);
PID1_Kd = register_point (#PID1_Kd);
s4 = make_spinner(#PID1_Kd, 2, 0.0, 1.0, 0.01, 0.1,
table, 1, 2, 2, 3);
box4.pack_start(table, TRUE, TRUE, 0);
frame = new(GtkFrame);
frame.set_color(1, GTK_STATE_NORMAL, 0xaaffaa);
frame.set_shadow_type(GTK_SHADOW_IN);
frame.add(box4);
box3.pack_start (frame, TRUE, TRUE, 5);
/********************************************************
* Plant Panel *
********************************************************/
box4 = new(GtkVBox);
box4.set_border_width(5);
make_prog_bar("PV:\nProcess Variable", box4, #PV_001, 0xeb0000);
table = gtk_table_new(2, 3, FALSE);
table.set_row_spacings(5);
table_labeler(table, "PROP", 0, 1, 0, 1);
PROP_001 = register_point (#PROP_001);
s5 = make_spinner(#PROP_001, 2, 0.1, 5.0, 0.01, 0.1,
table, 1, 2, 0, 1);
table_labeler(table, " INT", 0, 1, 1, 2);
INT_001 = register_point (#INT_001);
s6 = make_spinner(#INT_001, 2, 0.0, 10.0, 0.01, 0.1,
table, 1, 2, 1, 2);
table_labeler(table, " ", 0, 1, 2, 3);
table.set_row_spacing(1, 10);
box4.pack_start(table, TRUE, TRUE, 0);
frame = new(GtkFrame);
frame.set_color(1, GTK_STATE_NORMAL, 0xff9999);
frame.set_shadow_type(GTK_SHADOW_IN);
frame.add(box4);
box3.pack_start (frame, TRUE, TRUE, 5);
/********************************************************
* Radio buttons for setting PID Loop *
********************************************************/
box3 = new (GtkHBox);
box3.spacing = 0;
box3.border_width = 5;
label = new(GtkLabel);
label.set_text("PID Loop: ");
box3.pack_start (label, TRUE, TRUE, 0);
but1 = gtk_radio_button_new_with_label(nil, "Good");
box3.pack_start (but1, TRUE, TRUE, 0);
but1.signal("clicked",
`change_settings(@but1, t, @s1, 4, @s2, .05, @s3, .45,
@s4, .05, @s5, .70, @s6, 1.0, "3.8", nil));
but2 = gtk_radio_button_new_with_label(list(but1), "Poor");
box3.pack_start (but2, TRUE, TRUE, 0);
but2.signal("clicked",
`change_settings(@but2, t, @s1, 10, @s2, .05, @s3, .70,
@s4, .05, @s5, .70, @s6, 5.00, "3.9", nil));
but3 = gtk_radio_button_new_with_label(list(but1,but2), "Oscillating");
box3.pack_start (but3, TRUE, TRUE, 0);
but3.signal("clicked",
`change_settings(@but3, nil, @s1, 10, @s2, .05,
@s3, 1.37, @s4, .05, @s5, .70,
@s6, .55, "3.10", @ambutton));
but4 = gtk_radio_button_new_with_label(list(but1,but2,but3),
"Out-of-control");
box3.pack_start (but4, TRUE, TRUE, 0);
but4.signal("clicked",
`change_settings(@but4, nil, @s1, 10, @s2, 1.0,
@s3, 3.0, @s4, 1.0, @s5, .70,
@s6, .55, "3.11", @ambutton));
button = new(GtkButton);
button.label = "Exit";
button.signal("clicked", `(@win_monitor).destroy());
button.width = 40;
box3.pack_start(button, TRUE, TRUE, 0);
box2.pack_start (box3, TRUE, TRUE, 0);
but2.set_active(TRUE);
win_monitor.show_all();
win_monitor.reposition(20, 250);
win_monitor;
}
/*--------------------------------------------------------------------
* Function: main
* Returns: doesn't return
* Description: Calls the program_startup() function and loops.
*------------------------------------------------------------------*/
function main()
{
program_startup("monitor", "monq", #create_monitor());
/* Loop forever handling events. */
gtk_main();
}
Copyright © 1995-2010 by Cogent Real-Time Systems, Inc. All rights reserved.