/*-------------------------------------------------------------------- * File: phsimple.g * * Description: Cogent Tools Demo Tutorial One for Photon. * * Functions: * start_qnserves * program_startup * toggle_sym * change_settings * slider_callback * num_callback * accumulate_trends * update_trends * numeric_assign * create_monitor * main *------------------------------------------------------------------*/ /******************************************************** * STARTING UP * ********************************************************/ require_lisp("PhotonWidgets.lsp"); require_lisp("PhabTemplate.lsp"); PtInit(nil); /*-------------------------------------------------------------------- * 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) { /* This is the child process. */ exec ("phdhview", "-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"); } /* 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) { /* This is the child process. */ exec ("gamma", "emul"); exec("/bin/true"); } /* 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. */ usleep(10000); 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); } /******************************************************** * THE MONITOR * ********************************************************/ /*-------------------------------------------------------------------- * Function: toggle_sym * Returns: t or nil * Description: Toggles a database point between 0 and 1 when a button * is pressed. *------------------------------------------------------------------*/ function toggle_sym(sym) { set (sym, eval(sym) == 0 ? 1 : 0); write_point (sym, eval(sym)); } /*-------------------------------------------------------------------- * Function: change_settings * Returns: t or nil * Description: Used by the Monitor to change PID settings, and optionally * unclick the Auto button if it is on. The numeric widgets * won't execute callbacks after they have been changed * programmatically, so we have to write the point using * write_point(). *------------------------------------------------------------------*/ function change_settings(auto, n1, v1, n2, v2, n3, v3, n4, v4, n5, v5, n6, v6, msgno, autobutton?) { if (auto == nil) { if (AUTO_001 != 0) { autobutton.onoff_state = 0; write_point(#AUTO_001, 0); } } else { autobutton.onoff_state = 1; write_point(#AUTO_001, 1); n1.numeric_value = v1; write_point(#FREQ_001, v1); } if (_os_ == "QNX4") { n2.numeric_value = v2; n3.numeric_value = v3; n4.numeric_value = v4; n5.numeric_value = v5; n6.numeric_value = v6; } else { n2.numeric_value = v2 * 100; n3.numeric_value = v3 * 100; n4.numeric_value = v4 * 100; n5.numeric_value = v5 * 100; n6.numeric_value = v6 * 100; } write_point(#PID1_Kp, v2); write_point(#PID1_Ki, v3); write_point(#PID1_Kd, v4); write_point(#PROP_001, v5); write_point(#INT_001, v6); } /*-------------------------------------------------------------------- * Function: slider_callback * Returns: t or nil * Description: Attaches a callback and adds an exception function to * a slider widget. The callback writes a value to the * datahub point specified, and the exception function * assigns the value of the datahub point to the slider * widget whenever the value of the point changes in the * datahub. *------------------------------------------------------------------*/ function slider_callback(sld, !pnt) { PtAttachCallback(sld, Pt_CB_SLIDER_MOVE, `write_point(@pnt, ((@sld).gauge_value))); add_exception_function(eval(pnt), `(@sld).gauge_value = (@eval(pnt))); } /*-------------------------------------------------------------------- * Function: num_callback * Returns: t or nil * Description: Sets up a callback and an exception function for PtNumeric * widgets to deal with sending changed values to and from * the datahub. Assigns initial values to the widgets. *------------------------------------------------------------------*/ function num_callback(num, !pnt) { if(_os_ == "QNX4") { PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED, `write_point(@pnt, ((@eval(num)).numeric_value))); pnt = eval(pnt); num.numeric_value = eval(pnt); add_exception_function(pnt, `(@num).numeric_value = eval(@pnt)); } else { /* Handle an annoying characteristic in Photon 2 (OS is QNX 6) whose PtNumericFloat widget does not work properly in Gamma. Thus we need to use a PtNumericInteger widget, and convert back and forth to write points and receive exceptions. The widget that corresponds to FREQ_001 is a PtNumericInteger, which is already an integer.*/ if (eval(pnt) != #FREQ_001) { PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED, `write_point(@pnt, ((@eval(num)).numeric_value) / 100.0)); pnt = eval(pnt); num.numeric_value = round(eval(pnt) * 100); add_exception_function(pnt, `((@num).numeric_value = round(eval(@pnt) * 100))); } else { PtAttachCallback(num, Pt_CB_NUMERIC_CHANGED, `write_point(@pnt, ((@eval(num)).numeric_value))); pnt = eval(pnt); num.numeric_value = eval(pnt); add_exception_function(pnt, `(@num).numeric_value = eval(@pnt)); } } } /* Assign each DataHub point a property value, which in this case is an * array. These arrays will be used to hold a short history of each point, * allowing them to be plotted by the trend widget. */ setprop (#SP_001, #tdata, make_array (0)); setprop (#MV_001, #tdata, make_array (0)); setprop (#PV_001, #tdata, make_array (0)); /*-------------------------------------------------------------------- * Function: accumulate_trends * Returns: t or nil * Description: Adds new data to property value arrays. These arrays * are property values for the variables that correspond * to the DataHub points, and are used to update the trend * widget. *------------------------------------------------------------------*/ function accumulate_trends (syms...) { local data; with sym in syms do { data = getprop (sym, #tdata); data[length(data)] = eval (sym); } } /*-------------------------------------------------------------------- * Function: update_trends * Returns: t or nil * Description: Assigns the most recent arrays to the trend widget. *------------------------------------------------------------------*/ function update_trends (widget, syms...) { local tarray = make_array (length(syms)), i = 0, sym; with sym in syms do tarray[i++] = getprop (sym, #tdata); if(_os_ == "QNX4") widget.rttrend_data = tarray; else widget.trend_data = tarray; with sym in syms do shorten_array (getprop (sym, #tdata), 0); } /*-------------------------------------------------------------------- * Function: numeric_assign * Returns: t or nil * Description: Converts DataHub entries to integer values for use in * PtNumericInteger widgets. This is necessary because the * an irregularity in the Photon 2 PtNumericFloat widget * prevents it from working properly in Gamma. *------------------------------------------------------------------*/ function numeric_assign(num1, num2, num3, num4, num5) { if(_os_ == "QNX4") nil; else { num1.numeric_value = round(read_point(#PID1_Kp) * 100); num2.numeric_value = round(read_point(#PID1_Ki) * 100); num3.numeric_value = round(read_point(#PID1_Kd) * 100); num4.numeric_value = round(read_point(#PROP_001) * 100); num5.numeric_value = round(read_point(#INT_001) * 100); } } /*-------------------------------------------------------------------- * Function: create_monitor * Returns: doesn't return * Description: Creates the Monitor window. *------------------------------------------------------------------*/ function create_monitor () { local wfile, window, monitor_win, slidesp, progmv, progpv; local slideauto, slidedp, numkp, numki, numkd, numprop, numint; local butam, butgood, butpoor, butosc, butout, butx; if (_os_ == "QNX4") wfile = PhabReadWidgetFile("phmonitorwin/wgt/Ptmonitor.wgtw"); else wfile = PhabReadWidgetFile("ph2monitorwin/wgt/Ptmonitor.wgtw"); window = PhabCreateWidgets(wfile, nil, nil); monitor_win = PhabLookupWidget(window, #Ptmonitor, nil); slidesp = PhabLookupWidget(window, #PtSliderSP, nil); slideclr = PhabLookupWidget(window, #RtProgressColor, nil); numfreq = PhabLookupWidget(window, #PtNum1, nil); numkp = PhabLookupWidget(window, #PtNum2, nil); numki = PhabLookupWidget(window, #PtNum3, nil); numkd = PhabLookupWidget(window, #PtNum4, nil); numprop = PhabLookupWidget(window, #PtNum5, nil); numint = PhabLookupWidget(window, #PtNum6, nil); butgood = PhabLookupWidget(window, #PtToggleGood, nil); butpoor = PhabLookupWidget(window, #PtTogglePoor, nil); butosc = PhabLookupWidget(window, #PtToggleOscillating, nil); butout = PhabLookupWidget(window, #PtToggleOOC, nil); butam = PhabLookupWidget(window, #PtOnOffButtonAutomode, nil); labprop = PhabLookupWidget(window, #PtLabelPropVal, nil); progmv = PhabLookupWidget(window, #ProgressMV, nil); progpv = PhabLookupWidget(window, #ProgressPV, nil); trend1 = PhabLookupWidget(window, #Trend1, nil); slidesp.gauge_value = read_point(#SP_001); slideclr.gauge_value = read_point(#SP_001); progmv.gauge_value = read_point(#MV_001); progpv.gauge_value = read_point(#PV_001); butam.onoff_state = read_point(#AUTO_001); numfreq.numeric_value = read_point(#FREQ_001); numeric_assign(numkp, numki, numkd, numprop, numint); add_exception_function(#AUTO_001, `(@butam).onoff_state = AUTO_001); add_exception_function(#SP_001, `(@slideclr).gauge_value = SP_001); add_echo_function(#SP_001, `(@slideclr).gauge_value = SP_001); add_exception_function(#MV_001, `(@progmv).gauge_value = MV_001); add_exception_function(#PV_001, `(@progpv).gauge_value = PV_001); slider_callback (slidesp, #SP_001); num_callback (numfreq, #FREQ_001); num_callback (numkp, #PID1_Kp); num_callback (numki, #PID1_Ki); num_callback (numkd, #PID1_Kd); num_callback (numprop, #PROP_001); num_callback (numint, #INT_001); PtAttachCallback(butgood, Pt_CB_ACTIVATE, `change_settings(t, @numfreq, 4, @numkp, .05, @numki, .45, @numkd, .05, @numprop, .7, @numint, 1.0, 3.8, @butam)); PtAttachCallback(butpoor, Pt_CB_ACTIVATE, `change_settings(t, @numfreq, 10, @numkp, .05, @numki, .70, @numkd, .05, @numprop, .7, @numint, 5.0, 3.8, @butam)); PtAttachCallback(butosc, Pt_CB_ACTIVATE, `change_settings(nil, @numfreq, 10, @numkp, .05, @numki, 1.37, @numkd, .05, @numprop, .7, @numint, .54, 3.8, @butam)); PtAttachCallback(butout, Pt_CB_ACTIVATE, `change_settings(nil, @numfreq, 10, @numkp, .05, @numki, .45, @numkd, 1.00, @numprop, .7, @numint, .54, 3.8, @butam)); PtAttachCallback(butam, Pt_CB_ONOFF_NEW_VALUE, `toggle_sym(#AUTO_001)); butx = PhabLookupWidget(window, #Ptxbut, nil); PtAttachCallback(butx, Pt_CB_ACTIVATE, #exit_program(-1)); /* Set up the trend widget. We must set the trend resources in the * order below to ensure that the trend widget knows what to do with * the line colors. We want 3 traces, in the same colors that the * designer set when creating the slider and progress bars in the * Photon App Builder. */ trend1.trend_count = 3; trend1.trend_color_list = array (slideclr.progress_bar_color, progmv.progress_bar_color, progpv.progress_bar_color); trend1.trend_attributes = array (1, 2, 3); /* Animate trends every tenth of a second. The trend data arrays are * stored in the property lists of the associated database variables. * We can accumulate trend data faster than we display it by changing * the rates of accumulation and update. It does not make sense to display * trend data faster than about 27Hz, because the eye cannot distinguish * anything beyond that from smooth motion. In reality, 10Hz is plenty. */ every (0.1, #accumulate_trends (#SP_001, #MV_001, #PV_001)); every (0.1, #update_trends (trend1, #SP_001, #MV_001, #PV_001)); monitor_win.SetPos(20, 250); PtRealizeWidget(monitor_win); // PtMainLoop(); while(t) next_event(); } /*-------------------------------------------------------------------- * Function: main * Returns: doesn't return * Description: Calls the program_startup() function. *------------------------------------------------------------------*/ function main() { program_startup("monitor", "monq", #create_monitor()); }
Copyright © 1995-2010 by Cogent Real-Time Systems, Inc. All rights reserved.