Kitt Peak National Observaotry - Home Page


A New GUI for 4 meter
Oil and Floor Temperature Control



Click to view new GUI

Python Powered


The new OFTC GUI allows easy interaction when adjusting the floor and oil temperature control set point values, at the 4 meter telescope facility. The image below is a desktop screenshot of the GUI running remotely, on the mountain network machine, coral.

In the image, the system engineering diagram in the background, is called up using the DIAGRAM button on the bottom of the GUI. The diagram shows the basic system device relationships present in controlling the 4 meter telescope floor and oil temperatures. The diagram also displays the feedback telemetry labels and their relative locations within the OFTC system.

Click to view new GUI


The OFTC GUI sends and receives Forth commands in ASCII though a serial line to and from the blue OFTC controller box. The set point values are then used to control the volume of glycol flowing through the two heat exchanges, which have temperature monitor feedback controls stopping the glycol flow when the desired temperature setpoint has been achieved.

Click to view OFTC systems diagram


Current system setpoint values at the time the GUI is called up, are displayed in the "Current Setpoint =" line of the GUI. When a new temperature value is selected with the slider, the CHANGE button must be pressed to issue the change to the control device. As a safety, a dialog box will ask you "change set point?", and when aswered with "YES", issue the change in setpoint, to the controller.

The program was written in Python using the Tkinter (TK) interface for Python based GUI building. A secured version of the GUI's Python and Tkinter source code is shown below.




Python and Tkinter source code



#! /usr/local/bin/python

																					 
#                         Kitt Peak National Observatory                          	 
#                           4 meter Telescope Facility                            	 
#                                                                                 	 
#                      Oil and Floor Temperature Control GUI                       	 
#                                                                                 	 
#                                 Bill Gillespie                                   	 
#                                 November, 2002                                   	  
#
#																					   
#                                                                                 	   
#   This program provides graphical user interface interaction with the 4meter    	   
#   telescope facility "Oil and Floor - Temperature Control Device". The device   	   
#   is located on the ** floor in the ******* **** of the **************** at     	   
#   Kitt Peak.                                                                    	   
#                                                                                 	   
#   The method of interaction is communication via serial line with the oil and   	   
#   floor temperature control device. The serial line is accessed by reaching     	   
#   /*****/**** on the machine ***********.                                       	   
#
#   This program relies on the serial line program commands written by Scott      	   
#   Bulau, engineering dept at NOAO, Tucson. Those commands are written in        	   
#   Forth and allow ascii commands to be sent to the OFTC control box on the **   	   
#   floor. The device responds with reports of sensor temperatures, and setpoint  	   
#   values.                                                                       	   
#                                                                                 	   
#   The main purpose of this program is to allow easy changes of the two          	   
#   setpoint values that control the amount of cooling the dome floor and the     	   
#   telescope oil support receive. For an overview of the complete system click   	   
#   the "DIAGRAM" button on the GUI.                                              	   


#---------Fetch needed modules ---------------------------------------------#
#------------the serial module may have to be installed separately----------#

import serial, os, sys, string


#---------Set up the serial line for R/W -----------------------------------#
#(secured)

dev=serial.Serial('****/dev/*******', (****buad rate), (***num of data bits),( ***num of stop bits) , timeout= ****x)

#-------  Read the "osetpoint?" value into varialbe OSP --------------------#

def GetOilSetpointValue():
        dev.flush()
        dev.write('osetpoint?')
        dev.write('\r')
        OSP = dev.readlines() 
 
        global startingOSP
        
        stringOSP = OSP[0] 
        splitupOSP = stringOSP.split()  
        finalOSP = splitupOSP[1]
        startingOSP = int(finalOSP)
GetOilSetpointValue()  # returns the integer value of "Oil Setpoint" from the serial line.

#-------  Read the "fsetpoint?" value into variable FSP ---------------------#

def GetFloorSetpointValue():
        dev.flush()
        dev.write('fsetpoint?')
        dev.write('\r')
        FSP = dev.readlines()      
    
        global startingFSP

        stringFSP = FSP[0]
        splitupFSP = stringFSP.split()
        finalFSP = splitupFSP[1]            
        startingFSP = int(finalFSP)           

GetFloorSetpointValue()  # returns the integer value of "Floor Setpoint" from the serial device. 
        
        
		                					                                             
#                                 GUI SECTION      
                                                                                                        

from Tkinter import *
from tkMessageBox import *   

root = Tk()
root.title("OFTC Gui")

labelfont = ('helvetica', 13)

guiTitle = Label(root, relief=GROOVE, borderwidth=3,
                 text="4 meter Oil and Floor Temperature Control")
guiTitle.config(font= labelfont)
guiTitle.grid(row= 4, column=2, columnspan=40, rowspan=1,
              sticky=W+E+N+S)


noaoImage= PhotoImage(file="noao.gif")

def programInforamtion():
    showinfo("OFTC Info", "OFTC GUI\r4 meter Telescope\nKitt Peak\nNovemeber, 2002\nBill 
Gillespie\n")

noaoButton= Button(root, image=noaoImage, command= programInforamtion)
noaoButton.grid(row= 4, column=60, columnspan=1, rowspan=1,
               sticky=W+E+N+S)


NOAOimageSpacer1 = Label(root, text="   ")
NOAOimageSpacer1.grid(row=4, column=59)

TopGuiSpacer0 = Label(root, text="")
TopGuiSpacer0.grid(row=0, column=0, columnspan= 70)

BelowTitleGuiSpacer0 = Label(root, text="")
BelowTitleGuiSpacer0.grid(row=5, column=0, columnspan= 70)

RightSideSpacer1 = Label(root, text="   ")
RightSideSpacer1.grid(row=0, column=70, rowspan= 70)



#                       Oil Temperature - Program Section                          

oilCurrentLabel = Label(root, text="THE CURRENT OIL SETPOINT =")
oilCurrentLabel.grid(row=10, column=10, sticky=W)

oilValueLabel = Label(root, text= startingOSP)                                        
oilValueLabel.grid(row= 10, column=11, columnspan=1, rowspan=1, sticky=W)

oilLabel = Label(root, text="SELECT NEW OIL SETPOINT:")
oilLabel.grid(row=20, column=10, sticky=W)

oilSlider = Scale(root, from_= 2, to=8, 
            tickinterval=1, resolution=1, showvalue=YES, orient="horizontal")
oilSlider.grid(row= 20, column=11, columnspan=10, rowspan=1,
            sticky=W+E+N+S, padx=5, pady=5 )
oilSlider.set(startingOSP)


#   When answer YES to dialogue popup question "reset the oil temp?" this function called
#   it sends the value of the oil current slider to the serial line.
#   It redraws the label "oilValueLabel" with the new setpoint value from the slider.
#   It resets the global variable startingOSP to the newly commanded value for Oil Set Point.
  
 

def SendInNewOilSetpoint():
             newOSP = oilSlider.get()
             
             global startingOSP
                                
             startingOSP = newOSP
          
             dev.flush()
             dev.write('osetpoint='+ " " +`newOSP`)
             dev.write('\r')
 
             
             oilValueLabel = Label(root, text= newOSP)
             oilValueLabel.grid(row= 10, column=11, columnspan=1, rowspan=1,
             sticky=W)
                           
#   When answer NO to dialogue popup question "reset the oil temp?" this function called
#   it resets the slider value to the original oil setpoint value,
#  
#  

def NoSendInNewOilSetpoint():
             
             global startingOSP

             oilSlider.set(startingOSP)   # repositions slider back to current setpoint value.
             oilValueLabel = Label(root, text= startingOSP)
             oilValueLabel.grid(row= 10, column=11, columnspan=1, rowspan=1,
             sticky=W)

#   This function is called with the CHANGE button at the end of the
#   slider on the GUI. It calls up a "verification dialogue" to ask the
#   user if they do indeed want to issue the new value of the slider to
#   the device. 


def SendNewOilTempCheckPopup():
       if   askyesno("Verify Change", "Change oil setpoint?"):             
               showwarning("Yes", "Oil setpoint changed.")
               SendInNewOilSetpoint()

       else:      
               showinfo("No", "No change.")
               NoSendInNewOilSetpoint()

oilChange = Button(root, text="Change", command = SendNewOilTempCheckPopup)
oilChange.grid(row= 20, column=22, columnspan=1, rowspan=1)

CenterDivider0 = Label(root, text="")
CenterDivider0.grid(row=22, column=0)

CenterDivider0 = Label(root, text="")
CenterDivider0.grid(row=29, column=0)


#                        Floor Temperature - Program Section                       


floorCurrentLabel = Label(root, text="THE CURRENT FLOOR SETPOINT =")
floorCurrentLabel.grid(row=50, column=10, sticky=W)

floorValueLabel = Label(root, text= startingFSP)
floorValueLabel.grid(row= 50, column= 11, columnspan=1, rowspan=1,
              sticky=W)

floorLabel = Label(root, text= "SELECT NEW FLOOR SETPOINT:")
floorLabel.grid(row=60, column=10, sticky=W)

floorSlider = Scale(root, from_= -8, to= 2, tickinterval= 1, resolution= 1,
                  showvalue= YES, orient= "horizontal")
floorSlider.grid(row= 60, column=11, columnspan=10, rowspan=1, sticky=E+W, padx=5, pady=5 )
floorSlider.set(startingFSP)                                       # set slider value to serial 
value on start

# when answer YES to dialogue popup question "reset the floor temp?" this function called
# it sends the value of the floor slider to the serial line.
# and it redraws the label (it's value) for "current floor setpoint".


def SendInNewFloorSetpoint():
             newFSP = floorSlider.get()
     
             global startingFSP
             
             startingFSP = newFSP

             dev.flush()
             dev.write('fsetpoint='+ " " +`newFSP`)
             dev.write('\r')
       
             
             floorValueLabel = Label(root, text= newFSP)
             floorValueLabel.grid(row= 50, column=11, columnspan=1, rowspan=1,
             sticky=W)
  
  
# when answeer NO to dialogue popup question "reset the floor temp?" this function called
# it resets the slider value to the the serial line value of floor setpoint.
# and it redraws the label (it's value) for "current floor setpoint".


def NoSendInNewFloorSetpoint():
 
             global startingFSP
            
             floorSlider.set(startingFSP)   # repositions slider back to current setpoint value.
             floorValueLabel = Label(root, text= startingFSP)
             floorValueLabel.grid(row= 50, column=11, columnspan=1, rowspan=1,
             sticky=W)

####   Dialogue box popup function  (Calls on the two functions above)
####   This dialgoue asks user if they "really" want to command the change in setpoint"
####   As there are two choices - YES or NO - the are two functions that the two choices call.
####   They are: Yes =  SendInNewFloorSetpoint  and  No =  NoSendInNewFloorSetpoint   


def SendNewFloorTempCheckPopup():
        if   askyesno("Verify Change", "Change floor setpoint?"):
                showwarning("Yes", "Floor setpoint changed")
                SendInNewFloorSetpoint()
        else:      
                showinfo("No", "No change.")
                NoSendInNewFloorSetpoint()		
				

# change the temp by clicking "change" button - which calls function  SendNewFloorTempCheckPopup  
to verify change.



floorChange = Button(root, text="Change", command= SendNewFloorTempCheckPopup)
floorChange.grid(row= 60, column=22, columnspan=1, rowspan=1)

CenterDivider0 = Label(root, text="")
CenterDivider0.grid(row=63, column=0)

CenterDivider1 = Label(root, text="  ")
CenterDivider1.grid(row=75, column=0)


#                       Additional Buttons Section  
                            

def HelpWindowPopup():
      HelpWindow= Toplevel()
      Button(HelpWindow, text="Quit this window", command= HelpWindow.destroy).pack()
      HelpWindow.mainloop()

Helpbutton = Button(root, text="HELP", command= HelpWindowPopup)
Helpbutton.grid(row= 90, column=11)


def DiagramWindowPopup():
      DiagramWindow= Toplevel()
      gifdir = "../oftc/"
      diagramImage = PhotoImage(file=gifdir+"diagram.gif")  
      Button(DiagramWindow, image=diagramImage).pack()
      Button(DiagramWindow, text="Quit this window", command= DiagramWindow.destroy).pack()
      DiagramWindow.mainloop()

Diagrambutton = Button(root, text="DIAGRAM", command= DiagramWindowPopup)
Diagrambutton.grid(row= 90, column=12)


def GraphsWindowPopup():
      GraphsWindow= Toplevel()
      Button(GraphsWindow, text="Quit this window", command= GraphsWindow.destroy).pack()
      GraphsWindow.mainloop()

Graphsbutton = Button(root, text="GRAPHS", command= GraphsWindowPopup)
Graphsbutton.grid(row= 90, column=13)

def LogsWindowPopup():
      LogsWindow= Toplevel()
      Button(LogsWindow, text="Quit this window", command= LogsWindow.destroy).pack()
      LogsWindow.mainloop()

Logsbutton = Button(root, text="LOGS", command= LogsWindowPopup)
Logsbutton.grid(row= 90, column=14)


def URLWindowPopup():
      URLWindow= Toplevel()
      Button(URLWindow, text="Quit this window", command= URLWindow.destroy).pack()
      URLWindow.mainloop()

URLbutton = Button(root, text="URL", command= URLWindowPopup)
URLbutton.grid(row= 90, column=15)

def LegendWindowPopup():
      LegendWindow= Toplevel()
      Button(LegendWindow, text="Quit this window", command= LegendWindow.destroy).pack()
      LegendWindow.mainloop()

Legendbutton = Button(root, text="LEGEND", command= LegendWindowPopup)
Legendbutton.grid(row= 90, column=16)


import sys   # for sys.exit
def quitVerify():
     ans = askyesno('Verify exit', "Exit now?")
     if ans: sys.exit()
              
Quitbutton = Button(root, text="  QUIT   ",activebackground="darkgray",command= quitVerify)
Quitbutton.grid(row= 90, column=10, sticky=W,padx=5, pady=5 )

CenterDivider2 = Label(root, text="")
CenterDivider2.grid(row=100, column=0)

mainloop()



Bill Gillespie
Kitt Peak National Observatory
Observing Support Office
gillespie@noao.edu

Last update - Jan 02, 2003