;+
; NAME:
;     WATCHER
; PURPOSE:
;     Main monitoring routine, watches the DAILY.ARC file as records come
;     in to do various and sundry things as appropriate.
; CATEGORY:
;     OVRO APC REALTIME
; CALLING SEQUENCE:
;     watcher
; INPUTS:
; OPTIONAL (KEYWORD) INPUT PARAMETERS:
; ROUTINES CALLED:
;     sysdoy, openarc, getlrecn, getdata, tl_decode, fcal_update, truncate, fst_mkparm,
;     flaremeter, fst_dswrite, fst_dsread
; OUTPUTS:
; COMMENTS:
; SIDE EFFECTS:
;     This list will grow.  Receipt of segment types cause the following:
;       MONITOR     check V/F against current frequency calibration (FCAL_UPDATE)
;       EOF         truncate and delete the DAILY.ARC file (TRUNCATE)
; RESTRICTIONS:
; MODIFICATION HISTORY:
;     Written 26-Jul-1999 by Dale E. Gary
;     24-Jul-1999  DG
;       Added a command to clear the CPC's buffer on EOF, just before the
;       APC file is truncated.
;     31-Oct-1999  DG
;       Added windmeter to plot the wind speed and direction on receipt of a data record.
;     13-Nov-1999  DG
;       Changes to reflect new EOF handling.
;     18-Apr-2000  DG
;       Add automatic creation of TP FTS file and GIF file at end of
;       a solar scan.
;     12-Nov-2000  DG
;       Add automatic analysis of REFTPCAL (Cas A calibration).
;     29-Jan-2001  DG
;       Add automatic analysis of FCAL, GCAL
;     10-May-2001  DG
;       Possibly fixed long standing bug that caused routines to be called
;       with HREC = 0
;     24-Feb-2002  DG
;       Added call to FLAREMETER on receipt of a SOLAR scan header
;     12-Mar-2002  DG
;       Attempt to avoid startup problems with Flaremeter by starting it
;       on receipt of the first SOLAR data rather than on receipt of the
;       SOLAR header.
;     10-Jul-2003  DG
;       Added /HIDE keyword to SPAWN command to hide the command window.
;     18-Jul-2003  DG
;       Added code to avoid the infinite loop of calls to TRUNCATE when
;       DAILY.ARC is open by another process.
;     12-Jan-2004  DG
;       Added garbage collection call (HEAP_GC) to eliminate memory leaks.
;       Also made slight change to Windmeter (changes windbutton text)
;     17-Jan-2004  DG
;       Added printing of more information at HEAD_GC call.
;     21-Jul-2004  DG
;       PEAKUP (and perhaps others) seems to send an empty record at the end
;       instead of an EOS. The empty record is characterized by data values -1.
;       I added code to interpret all empty records as an EOS, so the analysis
;       routine will start for such cases.
;     07-Aug-2004  DG
;       Added check of TRAJECTORY segment, looking for SATELLITE sources.  If
;       found, and an appropriate satellite ephemeris file is found, then the
;       emphemeris data are written to the TRAJECTORY segment and posted to
;       the ARC file.
;     25-Jul-2005  DG
;       Fixed a bug with satellite trajectories, when there were no
;       satellite emphemeris files.
;     08-Jan-2006  DG
;       On receipt of a header, call FST_mkparm, which creates a parameter file
;		for use by the FASR Subsystem Testbed.
;     11-Apr-2006  DG
;       Add FST enable button, and provision to start and stop FST acquisitions.
;     14-Apr-2006  DG
;       Add check on whether FST command was received (once per second) and resend
;       if not.
;     02-Jun-2006  DG
;       Added provision for sending GEOMETRY structure via fst_write, and regularized
;       handling of FST to start on receipt of first DATA record after HEADER.
;     14-Jun-2006  DG
;       Move geometry handling to FST_MKPARM routine, and fix bug that caused FST START
;       command to be repeated over and over on receipt of data record.
;     11-Aug-2006  DG
;       Set default FSTbutton state to checked/on.
;     13-Nov-2006  DG
;       Changed to use FST_SOLAR_ATTRIBUTES
;     05-Dec-2006  DG
;       Fixed problem introduced on 13 Nov, when trying to go back to original solar
;       observing mode.
;     15-Dec-2006  DG
;       Sequence attribute was examined in two places--fixed the other one.
;-

PRO Watcher_Event, Event

  base = Event.handler
  stash = WIDGET_INFO(base, /CHILD)
  WIDGET_CONTROL, stash, GET_UVALUE=state, /NO_COPY

  IF (TAG_NAMES(Event, /STRUCTURE_NAME) EQ $
                            'WIDGET_KILL_REQUEST') THEN BEGIN
     IF (state.lun ne 0) THEN free_lun,state.lun
     ptr_free,state.fileptr
     WIDGET_CONTROL, Event.top, /DESTROY
     RETURN
  ENDIF

  CASE event.id OF

  state.clock: BEGIN

     ; Update the clock
     yr = strmid(systime(),20,4)
     doy = string(sysdoy(systime()),format='(I3.3)')
     timestr = ' Current Time: '+yr+'.'+doy+strmid(systime(),10,9)+' '
     WIDGET_CONTROL,state.clock,SET_VALUE=timestr
     WIDGET_CONTROL,state.clock,TIMER=1.0

     ; See if FST needs a repeat command
     CASE state.FST_Stat OF
       'start': BEGIN
                   if (state.scancode eq !scan.solar) then begin
                      att = fst_solar_attributes()
                      res = fst_dswrite('SETUP',attributes=att)
                      if (where(tag_names(att) eq 'SEQUENCE') ne -1) then res = fst_dswrite('DOSEQ',attributes={NAME:att.sequence.name})
                      res = fst_dswrite('Start',attributes={SCANDESC:att.scandesc})
                   endif else if (state.scancode eq !scan.satobs) then begin
                      scandesc = 'Satellite Observations'
                      res = fst_dswrite('Start',attribute={scandesc:scandesc})
                   endif else begin
                      scandesc = 'Unknown'
                      res = fst_dswrite('Start',attribute={scandesc:scandesc})
                   endelse
                   if (res ne 0) then begin
                      print,'FST did not receive START command. '+timestr
                      state.FST_stat='start'
                   endif else state.FST_stat='idle'
                END
        'stop': BEGIN
                   res = fst_dswrite('Stop')
                   if (res ne 0) then begin
                      print,'FST did not receive STOP command. '+timestr
                      state.FST_stat='stop'
                   endif else state.FST_stat='idle'
                END
        ELSE:
     ENDCASE

     ;if (state.FST) then print,state.FST_stat
     state.nticks = state.nticks+1
     if (state.nticks le state.maxticks) then GOTO, WRITE_STATE
     state.nticks = 0
     lun = state.lun
     ; Check whether DAILY.ARC exists.
     file = findfile(!DEFAULTS.DATADIR+'DAILY.ARC',count=exist)
     if (exist ne 0) then begin
        if (lun eq 0) then begin
           ; DAILY.ARC is not yet open, so open the file
           lun = openarc(file,a,nrec)

           if (lun eq 0) then GOTO, WRITE_STATE   ; File could not be opened (?)
           ; Read to the end of the file
           lastrec = getlrecn(a,nrec)

           ; Set these parameters into the STATE variable
           ptr_free,state.fileptr
           state.fileptr = ptr_new(a)
           state.lun = lun
           state.nrec = nrec
           state.currec = lastrec
        endif
        ; At this stage, STATE.CURREC should be at the last record unless new
        ; data are now available
        rec = state.currec
        a = *state.fileptr
        data = getdata(rec,a)
        while (data(0) gt 0) do begin
           ; Read through the records until we get to the end of the file
           tls = tl_decode(data)
           CASE tls.segmentcode OF
              !SEGM.HEADER: BEGIN
                    state.header = tls.nrc
                    ; If this is a SOLAR scan, set the Flaremeter switch, which will start
                    ; Flaremeter at the next data record.
                    if (tls.scancode eq !scan.solar) then state.fmeter = 1 else state.fmeter = 0
                    state.scancode = tls.scancode ; Save the SCANCODE for this header
                    ; Start the FST prep task, which will determine if the FST file needs updating
                    fst_mkparm,!DEFAULTS.DATADIR+'DAILY.ARC',state.header
                    if (state.FST and (tls.scancode eq !scan.solar or tls.scancode eq !scan.satobs)) then begin
                       ; FST button is checked and this is a solar or satobs scan, so set FST flag to
                       ; start FST at next data record
                       state.fst_stat = 'start'
                       state.fst_flag = 1
                    endif else begin
                       ; OTherwise clear FST flag and geometry pointer
                       state.fst_flag = 0
                    endelse
                 END
              !SEGM.MONITOR: fcal_update,data     ; Handle MONITOR segment
              !SEGM.SCRDUMP: BEGIN
                    if(tls.nrs eq 4) then screendmp,!defaults.datadir+'DAILY.ARC',rec-2
                 END
              !SEGM.DATA: BEGIN
                    if (state.fmeter) then begin
                        ; The Flaremeter switch is set, so unset it and start Flaremeter
                        state.fmeter = 0
                        flaremeter,!DEFAULTS.DATADIR+'DAILY.ARC',state.header,/auto
                    endif
                    if (state.fst_flag and state.FST_stat ne 'idle') then begin
                       ; The FST flag is set, so start FST
                       if (state.scancode eq !scan.solar) then begin
                          att = fst_solar_attributes()
                          res = fst_dswrite('SETUP',attributes=att)
                          if (where(tag_names(att) eq 'SEQUENCE') ne -1) then res = fst_dswrite('DOSEQ',attributes={NAME:att.sequence.name})
                          res = fst_dswrite('Start',attributes={SCANDESC:att.scandesc})
                       endif else if (state.scancode eq !scan.satobs) then begin
                          scandesc = 'Satellite Observations'
                          res = fst_dswrite('Start',attribute={scandesc:scandesc})
                       endif else begin
                          scandesc = 'Unknown'
                          res = fst_dswrite('Start',attribute={scandesc:scandesc})
                       endelse

                       if (res ne 0) then begin
                          print,'FST did not receive START command.'
                          state.FST_stat='start'
                       endif else state.FST_stat='idle'
                    endif
                    if (state.wind eq 1) then begin
                       ; Plot the wind
                       ; Open the windmeter if it is not already open
                       if (state.windid eq 0L) then begin
                          id = windmeter(/init,button='Plot',group=base)
                          state.windid = id
                       endif
                       id = state.windid
                       ; Get the wind speed and direction from the data record
                       wspeed = uint(data[20]) and 'FF'x
                       wdirn  = (ishft(uint(data[20]),-8) and 'FF'x)*2
                       wmsec = w2l(data,10)
                       ; Update the wind vector
                       ans = windmeter(id,wspeed,wdirn,wmsec)
                       if (ans eq 'Cancel') then begin
                          ; The user hit the Close button, so destroy the windmeter widget
                          ans = windmeter(id,/destroy)
                          state.windid = 0L
                          state.wind = 0
                       endif
                    endif
                 END
              !SEGM.EOS: BEGIN
eos:
                    hrec = state.header
                    if (hrec ne 0) then begin
                      no_match = 0
                      CASE tls.scancode OF
                      !SCAN.PEAKUP: begin
                          pkupchek,!defaults.datadir+'daily.arc',hrec,/debug
                          spawn,/hide,!defaults.cmddir+'cpcom /F='+!defaults.workdir+'pkuparm.fth'
                         end
                      !SCAN.LIMPOINT: limchek,!defaults.datadir+'daily.arc',hrec,/debug
                      !SCAN.SOLAR: begin
                          if (rec - hrec gt 100) then begin
                             cd,!defaults.webdir,current=cwd
                             tpanalyze,!defaults.datadir+'daily.arc',hrec,0.,[hrec,rec]
                             cd,cwd
                          endif
                         end
                      !SCAN.REFTPCAL: tpcalchek,!defaults.datadir+'daily.arc',hrec
                      !SCAN.FCAL: fcalchek,!defaults.datadir+'daily.arc',hrec
                      !SCAN.GCAL: gcalchek,!defaults.datadir+'daily.arc',hrec
                      ELSE: no_match=1
                      ENDCASE
                      ; Do garbage collection if one of the above scan types was found
                      if (no_match eq 0) then begin
                         print,'WATCHER: Finished scan type ',(tag_names(!scan))[tls.scancode],' at ',systime()
                         heap_gc
                      endif
                    endif
                    if (state.FST) then begin
                       res = fst_dswrite('Stop')
                       if (res ne 0) then begin
                          print,'FST did not receive STOP command.'
                          state.FST_stat = 'stop'
                       endif else state.FST_stat = 'idle'
                    endif
                 END
              !SEGM.EOF: BEGIN
                      ; Close the file
                      free_lun,state.lun
                      state.lun = 0
                      ; Free the now invalid file pointer
                      ptr_free,state.fileptr
                      state.fileptr = ptr_new(0)

                      ; Truncate the file
                      truncate,/auto,safe=safe
                      if (safe eq 1) then begin
                         ; TRUNCATE indicates that DAILY.ARC could not be closed, but it is safe to do so.
                         ; To keep WATCHER from a pointless infinite loop, halt it with a meaningful error
                         ; box until the problem is fixed.
                         help,/file,out=x
                         if (fix(total(strpos(strupcase(x),'DAILY.ARC'))) ge 1) then begin
                            ; It is WATCHER itself that is at fault.
                            ans = dialog_message(['WATCHER: TRUNCATE could not close DAILY.ARC because',$
                                                  'this process (WATCHER) has it open.  It is probably',$
                                   				  'just stuck.  You can safely close WATCHER and',$
                                   				  'manually delete DAILY.ARC, then open a new WATCHER.'],/error)
                         endif else begin
                            ; It is another process that is at fault.
                            ans = dialog_message(['WATCHER: TRUNCATE could not close DAILY.ARC because',$
                                   				  'another process has it open.  You can safely',$
                                   				  'manually delete DAILY.ARC, then click OK on this box',$
                                  				  'to continue.'],/error)

                         endelse
                      endif
                      GOTO, WRITE_STATE
                   END
              !SEGM.TRAJ: BEGIN
              		  ; Check if this is a SATELLITE trajectory.  If so, see if there is an
              		  ; ephemeris file with an appropriate time.  Finally, if there is such
              		  ; a file, read it and put the satellite ephemeris data into the
              		  ; TRAJECTORY segment, then delete the ephemeris file.  The ephemeris
              		  ; data will be read from the TRAJECTORY segment and applied by NEWSCAN.
              		  traj = decode(data,!SEGM.TRAJ)
              		  if (size(traj,/type) eq 8) then begin
              		     ; Decode was successful, so continue
               		     if (traj.srcname eq 'SATELLITE   ') then begin
               		        ; This is a SATELLITE trajectory, so continue
              		        ; Convert time to minutes of day
              		        daymin = nint(tls.msec/60000d)
              		        ; Get list of satellite files in EPHEM directory
              		        files = findfile(!defaults.ephemdir+'SATELLITE*.EPH',count=n)
         		            if (n ne 0) then begin
         		               fmin = indgen(n)
         		               ; Loop through files and determine minutes of day from file name
              		           for i = 0, n-1 do begin
              		              break_file,files[i],disk,dir,stem,ext
              		              reads,stem,hh,mm,format='(9x,I2,I2)'
              		              fmin[i] = hh*60 + mm
              		           endfor
              		           ; Find the times with positive difference relative to DAYMIN
              		           dif = fmin-daymin
              		           iposdif = where(dif gt 0,ndif)
              		           if (ndif gt 0) then begin
              		              ; Find the smallest positive time difference
              		              mindif = min(dif[iposdif],imin)
              		              k = iposdif[imin] ; Index of smallest positive time difference
              		              ; Nominally the difference should be 5 minutes, but allow up to 10
              		              if (mindif le 10) then begin
              		                 ; We have found an appropriate file, so open it, read it, and
              		                 ; then delete it
              		                 openr,/get,slun,files[k]
              		                 ha = 0.d & dec = 0.d & t = 0.d
              		                 hal = lonarr(3) & decl = lonarr(3) & tl = lonarr(3)
              		                 for i = 0, 2 do begin
              		                    readf,slun,ha,dec,t,format='(3f15.5)'
              		                    hal[i] = long(ha*10000.d)   ; Convert to 0.1 mdeg
              		                    decl[i] = long(dec*10000.d) ; Convert to 0.1 mdeg
              		                    tl[i] = long(t)
              		                 endfor
              		                 free_lun,slun
              		                 file_delete,files[k]
              		                 ; Now write the data values into the TRAJECTORY segment,
              		                 ; starting at location 18:
              		                 for i = 0, 2 do begin
              		                    data[18+6*i:19+6*i] = l2w(hal[i])
              		                    data[20+6*i:21+6*i] = l2w(decl[i])
              		                    data[22+6*i:23+6*i] = l2w(tl[i])
              		                 endfor
              		                 ; Open DAILY.ARC for writing
              		                 ulun = openarc(file,b,nrec,/update)
              		                 b(rec) = data   ; Write out the data record
              		                 free_lun,ulun   ; Close the file for writing
              		              endif
              		           endif
            		        endif
            		     endif
              		  endif
                   END
              ELSE: BEGIN
                      ; Interpret empty record (all data -1) as an EOS (since PEAKUP
                      ; seems to want to send an empty record instead of EOS).
                      if (tls.nro eq -1) then begin
                         ; Set the scancode to that of the previous header
                         tls.scancode = state.scancode
                         goto,eos
                      endif
                   END
           ENDCASE
           ; Try to read another record
           rec = rec + 1
           data = getdata(rec,a)
        endwhile
        state.currec = rec
     endif

        GOTO,WRITE_STATE
     END
  state.windbutton: BEGIN
        state.wind = 1 - state.wind
        if (state.wind eq 0 and state.windid ne 0L) then begin
           ans = windmeter(state.windid,/destroy)
           state.windid = 0L
           state.wind = 0
           widget_control,state.windbutton,set_value='Show Wind'
        endif else begin
           id = windmeter(/init,button='Plot',group=base)
           state.windid = id
           widget_control,state.windbutton,set_value='Close Wind'
        endelse
     END
  state.FSTbutton: BEGIN
        state.FST = 1 - state.FST
        ;print,state.FST?'ON':'OFF'
     END
  ENDCASE


WRITE_STATE:
  WIDGET_CONTROL, stash, SET_UVALUE=state, /NO_COPY

END


pro watcher,group=group

  IF N_ELEMENTS(Group) EQ 0 THEN GROUP=0

  scrsize = get_screen_size()
  wBase = WIDGET_BASE(GROUP_LEADER=Group, $
      COLUMN=1, $
      MAP=1, XOFFSET=scrsize[0]-183,YOFFSET=5,$
      /TLB_KILL_REQUEST_EVENTS,$
      TITLE='Watcher', $
      UVALUE='Watcher')

  BASE2 = WIDGET_BASE(wBase, $
      ROW=1, $
      MAP=1, $
      TITLE='ExtraBase', $
      UVALUE='BASE2')

  yr = strmid(systime(),20,4)
  doy = string(sysdoy(systime()),format='(I3.3)')
  timestr = ' Current Time: '+yr+'.'+doy+strmid(systime(),10,9)+' '

  font = 'Arial*bold*12'
  clock = WIDGET_LABEL(wBASE,/dynamic_resize,FONT=font,VALUE=timestr,FRAME=1)
  buttonbase = WIDGET_BASE(wBASE,ROW=1)
  windbutton = widget_button(buttonbase,VALUE='Show Wind',ysize=18)
  fstbase = widget_base(buttonbase,/nonexclusive,/row)
  fstbutton = widget_button(fstbase,VALUE='Enable FST')

  WIDGET_CONTROL, wBase, /REALIZE
  WIDGET_CONTROL,clock,TIMER=1.0
  WIDGET_CONTROL,fstbutton,/SET_BUTTON

  ; Connect the STATE structure to the first child of the base widget
  state = {CLOCK:clock, LUN:0, fileptr:ptr_new(0), NREC:0, CURREC:0,$
           nticks:0, maxticks:10, windid:0L, windbutton:windbutton, wind:0,$
           FSTbutton:FSTbutton, FST:1, FST_Stat:'idle', header:0, fmeter:0,$
           fst_flag:0, scancode:0}

  WIDGET_CONTROL, WIDGET_INFO(wBase, /CHILD), SET_UVALUE=state, /NO_COPY

  XMANAGER, 'Watcher', wBase, /NO_BLOCK

END
