/*-------------------------------------------------------------------- * 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.