import tkthread
import os
import pdb
from tkinter import *
from tkinter import ttk
import tkinter.font
import numpy as np
import yaml
from numpy.random import randint
from astropy.time import Time
from astropy.coordinates import EarthLocation, SkyCoord
from astropy.table import Table
import astropy.units as u
import pwi4_client
import weather
import spectemps
import influx
import aposong
import database
[docs]
class TelescopeWgt(ttk.Frame) :
def __init__(self,container) :
super().__init__(container)
row=1
ttk.Label(self,text="RA").grid(column=1,row=row,sticky=(W))
self.ra = StringVar()
ttk.Label(self, textvariable=self.ra).grid(column=2,row=row,sticky=(E),padx=15)
ttk.Label(self,text="AZ").grid(column=3,row=row,sticky=(W))
self.az = StringVar()
ttk.Label(self, textvariable=self.az).grid(column=4,row=row,sticky=(E),padx=15)
ttk.Label(self,text="UT").grid(column=5,row=row,sticky=(W))
self.ut = StringVar()
ttk.Label(self, textvariable=self.ut).grid(column=6,row=row,sticky=(E),padx=10)
row+=1
ttk.Label(self,text="DEC").grid(column=1,row=row,sticky=(W))
self.dec = StringVar()
ttk.Label(self, textvariable=self.dec).grid(column=2,row=row,sticky=(E),padx=15)
ttk.Label(self,text="ALT").grid(column=3,row=row,sticky=(W))
self.alt = StringVar()
ttk.Label(self, textvariable=self.alt).grid(column=4,row=row,sticky=(E),padx=15)
ttk.Label(self,text="LST").grid(column=5,row=row,sticky=(W))
self.lst = StringVar()
ttk.Label(self, textvariable=self.lst).grid(column=6,row=row,sticky=(E),padx=10)
row+=1
ttk.Label(self,text="PA").grid(column=1,row=row,sticky=(W))
self.pa = StringVar()
ttk.Label(self, textvariable=self.pa).grid(column=2,row=row,sticky=(E),padx=15)
ttk.Label(self,text="ROT").grid(column=3,row=row,sticky=(W))
self.rot = StringVar()
ttk.Label(self, textvariable=self.rot).grid(column=4,row=row,sticky=(E),padx=15)
ttk.Label(self,text="MJD").grid(column=5,row=row,sticky=(W))
self.mjd = StringVar()
ttk.Label(self, textvariable=self.mjd).grid(column=6,row=row,sticky=(E),padx=10)
row+=1
ttk.Label(self,text="HA").grid(column=1,row=row,sticky=(W))
self.ha = StringVar()
ttk.Label(self, textvariable=self.ha).grid(column=2,row=row,sticky=(E),padx=15)
ttk.Label(self,text="FOCUS").grid(column=3,row=row,sticky=(W))
self.focus = StringVar()
ttk.Label(self, textvariable=self.focus).grid(column=4,row=row,sticky=(E),padx=15)
ttk.Label(self,text="PORT").grid(column=5,row=row,sticky=(W))
self.port = StringVar()
ttk.Label(self, textvariable=self.port).grid(column=6,row=row,sticky=(E),padx=15)
[docs]
class DomeWgt(ttk.Frame) :
def __init__(self,container) :
super().__init__(container)
row=1
ttk.Label(self,text="SHUTTER").grid(column=1,row=row,sticky=(W))
self.shutter = StringVar()
ttk.Label(self, textvariable=self.shutter).grid(column=2,row=row,sticky=(E),padx=10)
ttk.Label(self,text="DOME AZ").grid(column=3,row=row,sticky=(W))
self.az = StringVar()
ttk.Label(self, textvariable=self.az).grid(column=4,row=row,sticky=(E),padx=10)
self.slewing = StringVar()
ttk.Label(self, textvariable=self.slewing).grid(column=5,row=1,sticky=(W),padx=10)
row+=1
ttk.Label(self,text="MIRROR COVERS").grid(column=1,row=row,sticky=(W))
self.coverstate = StringVar()
ttk.Label(self, textvariable=self.coverstate).grid(column=2,row=row,sticky=(E),padx=10)
ttk.Label(self,text="35M/25M").grid(column=3,row=row,sticky=(W))
self.stat35m = StringVar()
self.stat35m_label = ttk.Label(self, textvariable=self.stat35m)
self.stat35m_label.grid(column=4,row=row,sticky=(W,E),padx=10)
[docs]
class CameraWgt(ttk.Frame) :
def __init__(self,container) :
super().__init__(container)
ttk.Label(self,text="GCAM TEMP",width=9).grid(column=1,row=1,sticky=(W))
self.temperature = StringVar()
ttk.Label(self, textvariable=self.temperature).grid(column=2,row=1,sticky=(W,E),padx=10)
ttk.Label(self,text="COOLER POWER",width=15).grid(column=3,row=1,sticky=(W))
self.cooler = StringVar()
self.cooler_label = ttk.Label(self, textvariable=self.cooler)
self.cooler_label.grid(column=4,row=1,sticky=(W,E),padx=10)
ttk.Label(self,text="SCAM TEMP",width=9).grid(column=1,row=2,sticky=(W))
self.spec_temp = StringVar()
ttk.Label(self, textvariable=self.spec_temp).grid(column=2,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="COOLER POWER",width=15).grid(column=3,row=2,sticky=(W))
self.spec_cooler = StringVar()
self.spec_cooler_label = ttk.Label(self, textvariable=self.spec_cooler)
self.spec_cooler_label.grid(column=4,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="CHILLER",width=8).grid(column=5,row=2,sticky=(W))
self.chiller_temp = StringVar()
ttk.Label(self, textvariable=self.chiller_temp).grid(column=6,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="CHILLER FAULT",width=15).grid(column=7,row=2,sticky=(W))
self.chiller_fault = StringVar()
self.chiller_fault_label = ttk.Label(self, textvariable=self.chiller_fault)
self.chiller_fault_label.grid(column=8,row=2,sticky=(W,E),padx=10)
[docs]
class IodineWgt(ttk.Frame) :
def __init__(self,container) :
super().__init__(container)
ttk.Label(self,text="IODINE STAGE",width=16).grid(column=1,row=1,sticky=(W))
self.iodinestage = StringVar()
self.iodinestage_label = ttk.Label(self, textvariable=self.iodinestage)
self.iodinestage_label.grid(column=2,row=1,sticky=(W,E),padx=10)
ttk.Label(self,text="IODINE TEMP",width=16).grid(column=3,row=1,sticky=(W))
self.temp = StringVar()
ttk.Label(self, textvariable=self.temp).grid(column=4,row=1,sticky=(W,E),padx=10)
ttk.Label(self,text="IODINE VOLTAGE",width=16).grid(column=1,row=2,sticky=(W))
self.voltage = StringVar()
ttk.Label(self, textvariable=self.voltage).grid(column=2,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="IODINE CURRENT",width=16).grid(column=3,row=2,sticky=(W))
self.current = StringVar()
ttk.Label(self, textvariable=self.current).grid(column=4,row=2,sticky=(W,E),padx=10)
[docs]
class calWgt(ttk.Frame) :
def __init__(self,container) :
super().__init__(container)
ttk.Label(self,text="CALSTAGE",width=8).grid(column=1,row=1,sticky=(W))
self.calstage = StringVar()
self.calstage_label = ttk.Label(self, textvariable=self.calstage)
self.calstage_label.grid(column=2,row=1,sticky=(W,E),padx=10)
ttk.Label(self,text="MIRROR",width=8).grid(column=1,row=2,sticky=(W))
self.mirror = StringVar()
ttk.Label(self, textvariable=self.mirror).grid(column=2,row=2,sticky=(E),padx=10)
ttk.Label(self,text="QUARTZ",width=8).grid(column=3,row=2,sticky=(W))
self.quartz = StringVar()
self.quartz_label = ttk.Label(self, textvariable=self.quartz)
self.quartz_label.grid(column=4,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="LED",width=8).grid(column=5,row=2,sticky=(W))
self.led = StringVar()
self.led_label = ttk.Label(self, textvariable=self.led)
self.led_label.grid(column=6,row=2,sticky=(W,E),padx=10)
ttk.Label(self,text="ThAr",width=8).grid(column=7,row=2,sticky=(W))
self.thar = StringVar()
self.thar_label = ttk.Label(self, textvariable=self.thar)
self.thar_label.grid(column=8,row=2,sticky=(W,E),padx=10)
def postgres_bool(val) :
return '1' if val else '0'
def tel_ready() :
d=database.DBSession()
tab=d.query('robotic.status')
if tab['status'] == 'ready' :
return 1
elif tab['status'] == 'observing' :
return 2
elif tab['status'] == 'focus' :
return 3
else :
return 0
def postgres_write(telstatus,domestatus) :
tab=Table()
tab['tel_dome_id'] = [1]
tab['tel_ready_state'] = [tel_ready()]
tab['tel_con_state'] = [postgres_bool(telstatus.mount.is_connected)]
tab['tel_tracking'] = [postgres_bool(telstatus.mount.is_tracking)]
tab['tel_ra_j2000'] = [telstatus.mount.ra_j2000_hours]
tab['tel_dec_j2000'] = [telstatus.mount.dec_j2000_degs]
tab['tel_ra'] = [telstatus.mount.ra_apparent_hours]
tab['tel_dec'] = [telstatus.mount.dec_apparent_degs]
tab['tel_alt'] = [telstatus.mount.altitude_degs]
tab['tel_azm'] = [telstatus.mount.azimuth_degs]
tab['tel_alt_rms_error'] = [telstatus.mount.axis1.rms_error_arcsec]
tab['tel_azm_rms_error'] = [telstatus.mount.axis0.rms_error_arcsec]
tab['m3_pos'] = [telstatus.m3.port]
tab['dome_shutterstate'] = [domestatus.shutterstate]
tab['dome_az'] = [domestatus.az]
tab['dome_slewing'] = [postgres_bool(domestatus.slewing)]
tab['dome_light_state'] = [postgres_bool(domestatus.lights)]
tab['temp_m1'] = [None]
tab['temp_m2'] = [None]
tab['temp_m3'] = [None]
tab['temp_back'] = [None]
tab['temp_amb'] = [None]
tab['focuser_1_pos'] = [aposong.foc()]
tab['focuser_1_moving'] = [postgres_bool(aposong.F[0].IsMoving)]
#tab['focuser_2_pos'] = [aposong.specfoc()]
#tab['focuser_2_moving'] = [postgres_bool(aposong.F[4].IsMoving)]
tab['tel_lst'] = [telstatus.site.lmst_hours]
tab['ins_at' ] = [Time.now().fits]
d=database.DBSession(host='song1m_db.apo.nmsu.edu',database='db_apo',user='song')
d.ingest('public.tel_dome',tab,onconflict='update',constraintname='tel_dome_id')
d.close()
return tab
niter=0
if __name__ == '__main__' :
""" Start status window and updater
"""
aposong.init()
root = Tk()
default_font=tkinter.font.nametofont('TkDefaultFont')
default_font.configure(size=16,weight=tkinter.font.BOLD,family='Arial')
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1,minsize=200)
root.minsize(500,20)
root.geometry('+0+0')
root.resizable(False,False)
mainframe = ttk.Frame(root, padding="3 3 12 12",width=100,height=10)
mainframe.grid(column=0, row=0, stick=(N,W,E,S))
mainframe.columnconfigure(1, minsize=100, weight=1)
telframe=TelescopeWgt(mainframe)
telframe.grid(column=1,row=1,stick=(W))
for i in range(1,7) :
telframe.columnconfigure(i, minsize=50, weight=1)
line=ttk.Separator(mainframe,orient='horizontal')
line.grid(column=1,row=2,stick=(E,W))
domeframe=DomeWgt(mainframe)
domeframe.columnconfigure(1, minsize=200, weight=1)
domeframe.grid(column=1,row=3,stick=(W))
line=ttk.Separator(mainframe,orient='horizontal')
line.grid(column=1,row=4,stick=(E,W))
camframe=CameraWgt(mainframe)
camframe.grid(column=1,row=5,stick=(W))
line=ttk.Separator(mainframe,orient='horizontal')
line.grid(column=1,row=6,stick=(E,W))
iodineframe=IodineWgt(mainframe)
iodineframe.grid(column=1,row=7,stick=(W))
line=ttk.Separator(mainframe,orient='horizontal')
line.grid(column=1,row=8,stick=(E,W))
calframe=calWgt(mainframe)
calframe.grid(column=1,row=9,stick=(W))
line=ttk.Separator(mainframe,orient='horizontal')
line.grid(column=1,row=10,stick=(E,W))
for child in mainframe.winfo_children():
child.grid_configure(padx=5, pady=5)
mainframe.focus()
shutter=['Open','Closed','Opening','Closing','Error']
camerastate=['Idle','Waiting','Exposing','Reading','Download','Error']
coverstate=['NotPresent','Closed','Moving','Open','Unknown','Error']
apo=EarthLocation.of_site('APO')
guideok=True
domeok=True
telescopeok=True
ccdok=True
def update() :
global guideok, domeok, telescopeok, ccdok
#guideok = aposong.isguideok(guideok,recipients=aposong.config['test_recipients'])
domeok = aposong.isdomeok(domeok,recipients=aposong.config['test_recipients'])
telescopeok = aposong.istelescopeok(telescopeok,recipients=aposong.config['test_recipients'])
global niter
niter=(niter+1)%60
try :
# check for weather manual override
try :
override = weather.manual_override()
if override>0 :
aposong.override(override,verbose=False)
else :
aposong.override(0,verbose=False)
except :
aposong.override(0,verbose=False)
# weather status to influxDB
if niter%10 == 1 :
wdict=weather.getapo()
weather.influx_write(wdict)
weather.postgres_write(wdict)
except : print('error with weather')
try :
# spectrograph temperatures to influxDB
if niter%10 == 1 :
sdict=spectemps.get()
except : print('error with spectemps')
try :
if niter%60 == 1 :
ccdok = aposong.isccdok(ccdok,recipients=aposong.config['test_recipients'])
ccd_dict={}
for i in [0,3] :
try :
icam = aposong.getcam(i)
ccd_dict[f'camera_{i}_temp'] = aposong.C[icam].CCDTemperature
ccd_dict[f'camera_{i}_power'] = aposong.C[icam].CoolerPower
influx.write(ccd_dict,bucket='ccdtemp',measurement=f'ccd_{i}')
except :
print('error with camera: ',i)
continue
camframe.temperature.set('{:.1f}'.format(ccd_dict['camera_0_temp']))
camframe.cooler.set('{:.1f}'.format(ccd_dict['camera_0_power']))
if ccd_dict['camera_0_power'] < 0.1 or ccd_dict['camera_0_power'] > 98:
camframe.cooler_label.config(foreground='red')
else :
camframe.cooler_label.config(foreground='black')
camframe.spec_temp.set('{:.1f}'.format(ccd_dict['camera_3_temp']))
camframe.spec_cooler.set('{:.1f}'.format(ccd_dict['camera_3_power']))
if ccd_dict['camera_3_power'] < 0.1 or ccd_dict['camera_3_power'] > 98:
camframe.spec_cooler_label.config(foreground='red')
else :
camframe.spec_cooler_label.config(foreground='black')
ctemp = aposong.chiller()
camframe.chiller_temp.set('{:.1f}'.format(ctemp))
cfault = aposong.chiller_fault()
camframe.chiller_fault.set('{:d}'.format(cfault))
if cfault != 0 : camframe.chiller_fault_label.config(foreground='red')
else :camframe.chiller_fault_label.config(foreground='green3')
except : print('Error with camera')
try :
dict={}
dict['qhy_thermocouple_1'] = aposong.SW[aposong.getswitch('Yocto')].GetSwitchValue(0)
dict['qhy_thermocouple_2'] = aposong.SW[aposong.getswitch('Yocto')].GetSwitchValue(1)
influx.write(dict,bucket='ccdtemp',measurement='qhy_thermocouple')
except : pass
# table for postgres
motors=Table()
motors['motors_id'] = [1]
try :
# Get iodine cell related data
pos = aposong.iodine_position()
motors['iodine_pos'] = [pos]
temp = aposong.iodine_tget().replace('actual temperature: ','')
tset = aposong.iodine_tset().replace('set temperature: ','')
volt = aposong.iodine_get('voltage')
curr = aposong.iodine_get('current')
iodineframe.iodinestage.set(pos)
if abs(pos-aposong.config['iodinestage_in_pos']) < 0.2 : iodineframe.iodinestage_label.config(foreground='green3')
elif abs(pos-aposong.config['iodinestage_out_pos']) < 0.2 : iodineframe.iodinestage_label.config(foreground='blue')
else : iodineframe.iodinestage_label.config(foreground='yellow')
iodineframe.temp.set(temp+' / '+tset)
iodineframe.voltage.set(volt)
iodineframe.current.set(curr)
# parse temperature channels
tset1,tset2=tset.split()
temp1,temp2=temp.split()
volt1,volt2=volt.split()
curr1,curr2=curr.split()
if float(temp1)>float(tset1)+20 : #or float(temp2)>float(tset2)+20 :
# if temp is more than 20 degrees above set temp, disable heaters!
aposong.iodine_set('enable',0)
# table for postgres
motors['iodine1_temp_set'] = [tset1]
motors['iodine1_temp_read'] = [temp1]
motors['iodine1_current'] = [curr1]
motors['iodine1_voltage'] = [volt1]
motors['iodine2_temp_set'] = [tset2]
motors['iodine2_temp_read'] = [temp2]
motors['iodine2_current'] = [curr2]
motors['iodine2_voltage'] = [volt2]
# load into influx database
iodine_dict={}
# without second channel, don't load 99.999 into influx database
#for k,v in zip(['temp1','temp2','volt1','volt2','curr1','curr2'],
# [temp1,temp2,volt1,volt2,curr1,curr2]) :
# iodine_dict[k] = float(v)
for k,v in zip(['temp1','volt1','curr1'],
[temp1,volt1,curr1]) :
iodine_dict[k] = float(v)
influx.write(iodine_dict,bucket='iodinetemp',measurement='my_measurement')
except : print('error with iodine')
try :
pos =aposong.calstage_position()
calframe.calstage.set(pos)
if abs(pos-aposong.config['calstage_in_pos']) < 0.2 : calframe.calstage_label.config(foreground='green3')
elif abs(pos-aposong.config['calstage_out_pos']) < 0.2 : calframe.calstage_label.config(foreground='blue')
else : calframe.calstage_label.config(foreground='yellow')
# get eShel calibration status
lamps=np.zeros(4,dtype=bool)
state=np.zeros(4,dtype='U3')
for i in range(4) :
lamps[i] = aposong.SW[1].GetSwitch(i)
state[i] = 'On' if lamps[i] else 'Off'
calframe.mirror.set(state[3])
calframe.quartz.set(state[0])
if state[0] == 'On' : calframe.quartz_label.config(foreground='red')
else :calframe.quartz_label.config(foreground='black')
calframe.led.set(state[2])
if state[2] == 'On' : calframe.led_label.config(foreground='red')
else :calframe.led_label.config(foreground='black')
calframe.thar.set(state[1])
if state[1] == 'On' : calframe.thar_label.config(foreground='red')
else :calframe.thar_label.config(foreground='black')
motors['calib_mirror_pos'] = [pos]
motors['lamp_halogen_on'] = [1 if lamps[0] else 0]
motors['lamp_thar_on'] = [1 if lamps[1] else 0]
motors['lamp_led_on'] = [1 if lamps[2] else 0]
except : print('error with cal')
try :
t=Time(Time.now(),location=apo)
y,m,d,h,m,s=t.ymdhms
telframe.ut.set('{:02d}:{:02d}:{:04.1f}'.format(h,m,s))
h,m,s=t.sidereal_time('mean').hms
telframe.lst.set('{:02d}:{:02d}:{:04.1f}'.format(int(h),int(m),s))
telframe.mjd.set('{:.2f}'.format(t.mjd))
stat = aposong.telescope_status()
telframe.port.set('{:d}'.format(stat.m3.port))
ra = stat.mount.ra_j2000_hours
dec = stat.mount.dec_j2000_degs
c = SkyCoord(ra=ra*u.h, dec=dec*u.degree)
radec=c.to_string('hmsdms',sep=':',precision=1)
ras=radec.split()[0]
decs=radec.split()[1]
telframe.ra.set('{:s}'.format(ras))
telframe.dec.set('{:s}'.format(decs))
ha = (t.sidereal_time('mean')-ra*u.hourangle)
ha = (ha+12*u.hourangle)%(24*u.hourangle)-(12*u.hourangle)
h,m,s=ha.hms
telframe.ha.set('{:02d}:{:02d}:{:04.1f}'.format(int(h),int(m),s))
telframe.az.set('{:.2f}'.format(stat.Azimuth))
telframe.alt.set('{:.2f}'.format(stat.Altitude))
telframe.rot.set('{:.1f}'.format(stat.rotator.mech_position_degs))
telframe.pa.set('{:.1f}'.format(stat.rotator.field_angle_degs))
telframe.focus.set('{:d}'.format(aposong.foc()))
except :
telframe.ra.set('ERROR')
telframe.dec.set('ERROR')
telframe.pa.set('ERROR')
telframe.ha.set('ERROR')
telframe.az.set('ERROR')
telframe.alt.set('ERROR')
telframe.rot.set('ERROR')
telframe.port.set('ERROR')
telframe.focus.set('ERROR')
try :
domestat = aposong.domestatus()
domeframe.az.set('{:.1f}'.format(domestat.az))
domeframe.shutter.set('{:s}'.format(shutter[domestat.shutterstate]))
if domestat.slewing : domeframe.slewing.set('SLEWING')
else : domeframe.slewing.set(' ')
stat35m = aposong.S.Action('stat35m')
stat25m = aposong.S.Action('stat25m')
domeframe.stat35m.set(stat35m+'/'+stat25m)
if stat35m == 'open' or stat25m == 'open' :
domeframe.stat35m_label.config(foreground='green3')
else :
domeframe.stat35m_label.config(foreground='red')
domeframe.coverstate.set('{:s}'.format(coverstate[aposong.Covers.CoverState.value]))
except : print('error with dome')
try : postgres_write(stat,domestat)
except : pass
try :
d=database.DBSession(host='song1m_db.apo.nmsu.edu',database='db_apo',user='song')
#motors['spectrograph_foc'] = [aposong.specfoc()]
motors['ins_at' ] = [Time.now().fits]
d.ingest('public.motors',motors,onconflict='update',constraintname='motors_id')
d.close()
except : pass
root.after(5000,update)
import signal
def handler(signum,frame):
return
signal.signal(signal.SIGINT,handler)
root.after(1000,update)
#update()
root.mainloop()