;+
;WIDGET Procedure:
;  RECORDER
;PURPOSE:
; Widget tool that opens a socket and records streaming data from a server (host) and can save it to a file
; or send to a user specified routine. This tool runs in the background.
; Keywords:
;   SET_FILE_TIMERES : defines how often the current output file will be closed and a new one will be opened
;   DIRECTORY:  string prepended to fileformat when opening an output file.
; Author:
;    Davin Larson - April 2011
;
; $LastChangedBy: ali $
; $LastChangedDate: 2021-09-24 16:07:57 -0700 (Fri, 24 Sep 2021) $
; $LastChangedRevision: 30319 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/trunk/general/tools/misc/recorder.pro $
;
;-

PRO recorder_event, ev   ; recorder
  ;   on_error,1

  widget_control, ev.top, get_uvalue= info   ; get all widget ID's
  wids = info.wids
  localtime=1
  dlevel=info.dlevel

  CASE ev.id OF                         ;  Timed events
    wids.base:  begin
      if info.hfp gt 0 then begin
        on_ioerror, stream_error
        eofile =0
        info.time_received = systime(1)
        widget_control,wids.base,set_uvalue=info
        buffer= bytarr(info.maxsize)
        b=buffer[0]
        for i=0L,n_elements(buffer)-1 do begin                       ; Read from stream one byte (or value) at a time
          flag = file_poll_input(info.hfp,timeout=0)
          if flag eq 0 then break
          ;                if eof(info.hfp) then begin            ; This should be fixed so that it does not crash when disconnect by peer.
          ;                    widget_control,wids.host_text,get_value=hostname
          ;                    widget_control,wids.host_port,get_value=hostport
          ;                    dprint,dlevel=dlevel-1,info.title_num+'Connection to Host: '+hostname[0]+':'+hostport[0]+' broken. ',i
          ;                    eofile = 1
          ;                    break
          ;                endif
          readu,info.hfp,b
          buffer[i] = b
        endfor
        if eofile eq 1 then begin
          stream_error:
          widget_control,wids.host_text,get_value=hostname
          widget_control,wids.host_port,get_value=hostport
          dprint,dlevel=dlevel-1,info.title_num+'File error: '+hostname[0]+':'+hostport[0]+' broken. ',i
          dprint,dlevel=dlevel,phelp=2,!error_state
        endif

        ;;   Switch file name if needed
        if info.file_timeres ne 0 then begin
          if info.time_received ge info.next_filechange then begin
            dprint,dlevel=dlevel,time_string(info.time_received,prec=3)+ ' Time to change files.'
            if info.dfp then begin
              recorder_event,{top: ev.top, id:wids.dest_button}   ; close old file  - possible error that dfp might change!
              recorder_event,{top: ev.top, id:wids.dest_button}   ; open  new file
            endif
          endif
          widget_control, ev.top, get_uvalue= info   ; get all widget ID's
          info.next_filechange = info.file_timeres * ceil(info.time_received / info.file_timeres)
          widget_control,wids.base,set_uvalue=info
        endif

        if i gt 0 then begin                      ;; process data
          buffer = buffer[0:i-1]
          if keyword_set(info.dfp) then writeu,info.dfp, buffer  ;swap_endian(buffer,/swap_if_little_endian)
          flush,info.dfp
          msg = string(/print,i,buffer[0:(i < 64)-1],format='(i6 ," bytes: ", 128(" ",Z02))')
          msg = time_string(info.time_received,tformat='hh:mm:ss - ',local=localtime) + msg
        endif else begin
          dummy = temporary(buffer)    ;           buffer=0
          msg =time_string(info.time_received,tformat='hh:mm:ss - No data available',local=localtime)
        endelse
        if widget_info(/button_set,wids.proc_button) then begin
          widget_control,wids.proc_name,get_value = proc_name
          if keyword_set(proc_name) then call_procedure,proc_name[0],buffer ,info=info  ;,time=info.time_received   ; Execute exec_proc here
        endif
        widget_control,wids.output_text,set_value=msg
        dprint,dlevel=dlevel+3,info.title_num+msg,/no_check
        widget_control,wids.poll_int,get_value = poll_int
        poll_int = float(poll_int)
        if poll_int le 0 then poll_int = 1
        if 1 then begin
          poll_int = poll_int - (systime(1) mod poll_int)  ; sample on regular boundaries
        endif

        if not keyword_set(eofile) then WIDGET_CONTROL, wids.base, TIMER=poll_int else widget_control,wids.host_button,timer=2
      endif
      return
    end
    wids.host_button : begin
      widget_control,wids.host_button,get_value=status
      widget_control,wids.host_text, get_value=server_name
      widget_control,wids.host_port, get_value=server_port
      server_n_port = server_name+':'+server_port
      case status of
        'Connect to': begin
          *info.buffer_ptr = !null                                  ; Get rid of previous buffer contents cache
          WIDGET_CONTROL, wids.host_button, set_value = 'Connecting',sensitive=0
          WIDGET_CONTROL, wids.host_text, sensitive=0
          WIDGET_CONTROL, wids.host_port, sensitive=0
          socket,hfp,/get_lun,server_name,fix(server_port),error=error ,/swap_if_little_endian,connect_timeout=10
          if keyword_set(error) then begin
            dprint,dlevel=dlevel-1,info.title_num+!error_state.msg,error   ;strmessage(error)
            widget_control, wids.output_text, set_value=!error_state.msg
            WIDGET_CONTROL, wids.host_button, set_value = 'Failed:',sensitive=1
            WIDGET_CONTROL, wids.host_text, sensitive=1
            WIDGET_CONTROL, wids.host_port, sensitive=1
          endif else begin
            dprint,dlevel=dlevel,info.title_num+'Connected to server: "'+server_n_port+'"  Unit:'+strtrim(hfp)
            info.hfp = hfp
            WIDGET_CONTROL, wids.base, TIMER=1    ; , set_uvalue=hfp
            WIDGET_CONTROL, wids.host_button, set_value = 'Disconnect',sensitive=1
          endelse
        end
        'Disconnect': begin
          WIDGET_CONTROL, wids.host_button, set_value = 'Closing'  ,sensitive=0
          WIDGET_CONTROL, wids.host_text, sensitive=1
          WIDGET_CONTROL, wids.host_port, sensitive=1
          msg = 'Disconnected from server: "'+server_n_port+'"'
          widget_control, wids.output_text, set_value=msg
          dprint,dlevel=dlevel,info.title_num+msg
          free_lun,info.hfp
          info.hfp =0
          wait,1
          WIDGET_CONTROL, wids.host_button, set_value = 'Connect to',sensitive=1
        end
        else: begin
          WIDGET_CONTROL, wids.host_text, sensitive=1
          WIDGET_CONTROL, wids.host_port, sensitive=1
          WIDGET_CONTROL, wids.host_button, set_value = 'Connect to',sensitive=1
          dprint,info.title_num+'Error Recovery'
        end
      endcase
    end
    wids.dest_button: begin
      widget_control,ev.id,get_value=status

      widget_control,wids.dest_text, get_value=filename
      case status of
        'Write to': begin
          if keyword_set(info.dfp) then begin
            free_lun,info.dfp
            info.dfp = 0
          endif
          WIDGET_CONTROL,   ev.id       , set_value = 'Opening' ,sensitive=0
          widget_control, wids.dest_text, get_value = fileformat,sensitive=0
          filename = time_string(systime(1),tformat = fileformat[0])                     ; Substitute time string
          widget_control,wids.host_text, get_value=hostname
          filename = str_sub(filename,'{HOST}',strtrim(hostname,2) )
          widget_control,wids.host_port, get_value=hostport
          filename = str_sub(filename,'{PORT}',strtrim(hostport,2) )               ; Substitute port number
          widget_control, wids.dest_text, set_uvalue = fileformat,set_value=filename
          if keyword_set(filename) then begin
            file_open,'u',info.directory+filename, unit=dfp,dlevel=4,compress=-1,file_mode='666'o,dir_mode='777'o
            dprint,dlevel=dlevel,info.title_num+'Opened output file: '+file_info_string(info.directory+filename)+' Unit:'+strtrim(dfp)
            info.dfp = dfp
            info.filename= info.directory+filename
            widget_control, wids.dest_flush, sensitive=1
          endif
          ;              wait,1
          WIDGET_CONTROL, ev.id, set_value = 'Close   ',sensitive =1
        end
        'Close   ': begin
          WIDGET_CONTROL, ev.id,          set_value = 'Closing',sensitive=0
          widget_control, wids.dest_flush, sensitive=0
          widget_control, wids.dest_text ,get_uvalue= fileformat,get_value=filename
          if info.dfp gt 0 then begin
            free_lun,info.dfp
            info.dfp =0
          endif
          ;            wait,1
          widget_control, wids.dest_text ,set_value= fileformat,sensitive=1
          WIDGET_CONTROL, ev.id, set_value = 'Write to',sensitive=1
          dprint,dlevel=dlevel,info.title_num+'Closed output file: '+file_info_string(info.directory+filename),no_check_events=1
        end
        else: begin
          dprint,info.title_num+'Invalid State'
        end
      endcase
    end
    wids.dest_flush: begin
      recorder_event,{top: ev.top, id:wids.dest_button}   ; close old file
      recorder_event,{top: ev.top, id:wids.dest_button}   ; open  new file
    end
    ;    wids.host_text:  begin
    ;        widget_control,ev.id,get_value=value
    ;        dprint,'"'+value+'"'
    ;    end
    wids.proc_button: begin
      widget_control,wids.proc_name,get_value=proc_name
      widget_control,wids.proc_name,sensitive = (ev.select eq 0)
      info.run_proc = ev.select
      dprint,dlevel=2,info.title_num+'"'+proc_name+ '" is '+ (info.run_proc ? 'ON' : 'OFF')
    end
    wids.done: begin   ;    'DONE' ;  close files here!
      if info.hfp gt 0 then begin
        fs = fstat(info.hfp)
        dprint,dlevel=dlevel-1,info.title_num+'Closing '+fs.name
        free_lun,info.hfp
      endif
      if info.dfp gt 0 then begin
        fs = fstat(info.dfp)
        dprint,dlevel=dlevel-1,info.title_num+'Closing '+fs.name
        free_lun,info.dfp
      endif
      ptr_free,ptr_extract(info)
      WIDGET_CONTROL, ev.TOP, /DESTROY
      dprint,dlevel=dlevel-1,info.title_num+'Widget Closed'
      return
    end
    else: begin
      msg = string('Base ID is: ',wids.base)
      widget_control, wids.output_text, set_value=msg
      dprint,info.title_num+msg
      printdat,ev
      printdat,info
    end
  ENDCASE
  widget_control,wids.base,set_uvalue=info
END


PRO exec_proc_template,buffer,info=info
  ;    savetomain,buffer
  ;    savetomain,time

  n = n_elements(buffer)
  if n ne 0 then  begin
    if debug(2) then begin
      dprint,time_string(info.time_received,prec=3) +''+ strtrim(n_elements(buffer))
      n = n_elements(buffer) < 512
      hexprint,buffer[0:n-1]    ;,swap_endian(uint(buffer,0,n_elements(buffer)/2))
    endif
  endif else print,format='(".",$)'

  return
end


PRO recorder,base,title=title,ids=ids,host=host,port=port,destination=destination,exec_proc=exec_proc, $
  set_connect=set_connect, set_output=set_output, pollinterval=pollinterval, set_file_timeres=set_file_timeres ,$
  get_procbutton = get_procbutton,set_procbutton=set_procbutton,directory=directory, $
  get_filename=get_filename,info=info
  if ~(keyword_set(base) && widget_info(base,/managed) ) then begin
    if not keyword_set(host) then host = 'localhost'
    if not keyword_set(port) then port = '2022'
    if not keyword_set(title) then title = 'Socket Recorder'
    port=strtrim(port,2)
    if not keyword_set(destination) then destination = 'socket_{HOST}.{PORT}_YYYYMMDD_hhmmss.dat'
    ids = create_struct('base', WIDGET_BASE(/COLUMN, title=title ) )
    ids = create_struct(ids,'host_base',   widget_base(ids.base,/row, uname='HOST_BASE') )
    ids = create_struct(ids,'host_button', widget_button(ids.host_base, uname='HOST_BUTTON',value='Connect to') )
    ids = create_struct(ids,'host_text',   widget_text(ids.host_base,  uname='HOST_TEXT' ,VALUE=host ,/EDITABLE ,/NO_NEWLINE ) )
    ids = create_struct(ids,'host_port',   widget_text(ids.host_base,  uname='HOST_PORT',xsize=6, value=port   , /editable, /no_newline))
    ids = create_struct(ids,'poll_int' ,   widget_text(ids.host_base,  uname='POLL_INT',xsize=6,value='1',/editable,/no_newline))
    ;    if n_elements(directory) ne 0 then $
    ;      ids = create_struct(ids,'destdir_text',   widget_text(ids.base,  uname='DEST_DIRECTORY',xsize=40 ,/EDITABLE ,/NO_NEWLINE  ,VALUE=directory))
    ids = create_struct(ids,'dest_base',   widget_base(ids.base,/row, uname='DEST_BASE'))
    ids = create_struct(ids,'dest_button', widget_button(ids.dest_base, uname='DEST_BUTTON',value='Write to'))
    ids = create_struct(ids,'dest_text',   widget_text(ids.dest_base,  uname='DEST_TEXT',xsize=40 ,/EDITABLE ,/NO_NEWLINE  ,VALUE=destination))
    ids = create_struct(ids,'dest_flush',  widget_button(ids.dest_base,uname='DEST_FLUSH', value='New' ,sensitive=0))
    ids = create_struct(ids,'output_text', WIDGET_TEXT(ids.base, uname='OUTPUT_TEXT'))
    ids = create_struct(ids,'proc_base',   widget_base(ids.base,/row, uname='PROC_BASE'))
    ids = create_struct(ids,'proc_base2',  widget_base(ids.proc_base ,/nonexclusive))
    ids = create_struct(ids,'proc_button', widget_button(ids.proc_base2,uname='PROC_BUTTON',value='Procedure:'))
    ids = create_struct(ids,'proc_name',   widget_text(ids.proc_base,xsize=35, uname='PROC_NAME', value = keyword_set(exec_proc) ? exec_proc :'exec_proc_template',/editable, /no_newline))
    ids = create_struct(ids,'done',        WIDGET_BUTTON(ids.proc_base, VALUE='Done', UNAME='DONE'))
    title_num = title+' ('+strtrim(ids.base,2)+'): '
    info = {wids:ids, $
      ;      hostname:host, hostport:port, $
      title: title, $
      title_num: title_num, $
      time_received: 0d,  $
      file_timeres: 0d,   $
      next_filechange: 0d, $
      hfp:0,  $
      directory:'' ,  $
      fileformat:destination,  $
      filename:'', $
      dfp:0 , $
      maxsize:2L^20, $
      buffer_ptr: ptr_new(!null),   $
      ;        pollinterval:1., $
      verbose:2, $
      dlevel: 2, $
      exec_proc_ptr: ptr_new(!null), $
      run_proc:keyword_set(set_procbutton) }

    ;    info.buffer_ptr = ptr_new( bytarr( info.maxsize ) )
    WIDGET_CONTROL, ids.base, SET_UVALUE=info
    WIDGET_CONTROL, ids.base, /REALIZE
    widget_control, ids.base, base_set_title=title_num
    XMANAGER, 'recorder', ids.base,/no_block
    dprint,dlevel=dlevel,info.title_num+'Widget started'
    base = ids.base
  endif else begin
    widget_control, base, get_uvalue= info   ; get all widget ID's
    ids = info.wids
  endelse
  if size(/type,exec_proc) eq 7 then    widget_control,ids.proc_name,set_value=exec_proc
  if size(/type,destination) eq 7 then  widget_control,ids.dest_text,set_value=destination
  if size(/type,host) eq 7 then  widget_control,ids.host_text,set_value=host
  if n_elements(port) eq 1 then  widget_control,ids.host_port,set_value=strtrim(port,2)
  if n_elements(pollinterval) ne 0 then widget_control,ids.poll_int,set_value=strtrim(pollinterval,2)
  if n_elements(set_output)  eq 1 && (keyword_set(info.dfp) ne keyword_set(set_output )) then recorder_event, { id:ids.dest_button, top:ids.base }
  if n_elements(set_connect) eq 1 && (keyword_set(info.hfp) ne keyword_set(set_connect)) then recorder_event, { id:ids.host_button, top:ids.base }
  if n_elements(set_procbutton) eq 1 then begin
    widget_control,ids.proc_button,set_button=set_procbutton
    recorder_event, { top:ids.base, id:ids.proc_button, select: keyword_set(set_procbutton) }
  endif
  if n_elements(set_file_timeres) then begin
    info.file_timeres = set_file_timeres
    widget_control, base, set_uvalue= info
  endif
  if n_elements(directory) then begin
    info.directory = directory
    widget_control, base, set_uvalue= info
  endif
  get_procbutton = widget_info(ids.proc_button,/button_set)
  ;widget_control,ids.dest_text,get_value=get_filename
  get_filename = keyword_set(info.dfp) ? info.filename : ''


END
