; $LastChangedBy: davin-mac $
; $LastChangedDate: 2023-11-13 07:46:41 -0800 (Mon, 13 Nov 2023) $
; $LastChangedRevision: 32242 $
; $URL: svn+ssh://thmsvn@ambrosia.ssl.berkeley.edu/repos/spdsoft/tags/spedas_6_1/projects/SWFO/STIS/packet_reader__define.pro $



function packet_reader::header_struct,buf
  dprint,dlevel=5,verbose=self.verbose,buf ;,dwait=1
  
  if n_elements(buf) lt self.header_size then return,!null
  sync = self.source_dict.sync_pattern
  nsync = n_elements(sync)
  if nsync ne 0 && array_equal(buf,sync) eq 0 then return, !null
  pkt_size = buf[nsync+4] * 256u + buf[nsync+5] + 7u         ; valid for CCSDS
  seqn = ( buf[nsynq+2] *256u + buf[nsync+3] ) and '3F'xu
  
  hdr_struct = {  time:0d, apid:0u,   psize:psize, seqn:seqn }  
  
 
   return,hdr_struct

end








;+
;  PROCEDURE SWFO_GSEMSG_Buffer_READ
;  This procedure is only specific to SWFO in the "sync bytes" found in the MSG header.  Otherwise it could be considered generic
;  It purpose is to read bytes from a previously opened MSG file OR stream.  It returns at the end of file (for files) or when
;  no more bytes are available for reading from a stream.
;  It should gracefully handle sync errors and find sync up on a MSG header.
;  When a complete MSG header and its enclosed CCSDS packet are read in, it will execute the routine "swfo_ccsds_spkt_handler"
;-

pro packet_reader::read,source  ,source_dict=parent_dict

  dwait = 10.

  if isa(parent_dict,'dictionary') &&  parent_dict.haskey('cmbhdr') then begin
    header = parent_dict.cmbhdr
    dprint,dlevel=4,verbose=self.verbose,header.description,'  ',header.size
  endif else begin
    dprint,verbose=self.verbose,dlevel=3,'No cmbhdr'
    header = {time: !values.d_nan, seqn:0U, size:0u , gap:0 }
  endelse


  ;printdat,source,/hex
  source_dict = self.source_dict
  dict = self.source_dict

  if ~source_dict.haskey('fifo') then begin
    dict.fifo = !null    ; this contains the unused bytes from a previous call
    dict.flag = 1
    ;self.verbose=3
  endif


  ;if ~source_dict.haskey('sync_ccsds_buf') then begin
  ;  source_dict.sync_ccsds_buf = !null   ; this contains the contents of the buffer from the last call
  ;endif
  ;run_proc=1

  ;if ~source_dict.haskey('remainder_gsemsg') then begin
  ;  source_dict.remainder_gsemsg = !null
  ;endif

  if debug(2,self.verbose) then begin
    if abs(fix(header.seqn - 3625)) lt 6  || ( header.size ne 134 && header.size ne 30 && header.size ne 268) then begin    ; trap to find problem
      dprint,header
      dprint
      dict.flag = 1
    endif

  endif



  if debug(4,self.verbose,msg='test') then begin
    ;printdat,source_dict
    print,n_elements(source_dict.sync_ccsds_buf),n_elements(source)
    hexprint,source
  endif

  on_ioerror, nextfile
  time = systime(1)
  source_dict.time_received = time

  msg = time_string(source_dict.time_received,tformat='hh:mm:ss.fff -',local=localtime)

  ;remainder = !null
  ;remainder = source_dict.remainder_gsemsg
  ;source_dict.remainder_gsemsg = !null
  nbytes = 0UL
  sync_errors =0ul
  total_bytes = 0L
  endofdata = 0
  while ~endofdata do begin

    if dict.fifo eq !null then begin
      dict.n2read = 6
      dict.header_is_valid = 0
      dict.packet_is_valid = 0
    endif
    nb = dict.n2read

    buf= self.read_nbytes(nb,source,pos=nbytes)
    nbuf = n_elements(buf)

    if nbuf eq 0 then begin
      dprint,verbose=self.verbose,dlevel=4,'No more data'
      break
    endif

    bytes_missing = nb - nbuf   ; the number of missing bytes in the read

    dict.fifo = [dict.fifo,buf]
    nfifo = n_elements(dict.fifo)

    if bytes_missing ne 0 then begin
      dict.n2read = bytes_missing
      if ~isa(buf) then endofdata =1
      continue
    endif

    if dict.header_is_valid eq 0 then begin
      sync_pattern = ['A8'xb,'29'xb,'0'xb]

      if (nfifo lt 6) || array_equal(dict.fifo[0:2],sync_pattern) eq 0 then begin
        dict.fifo = dict.fifo[1:*]
        dict.n2read = 1
        nb = 1
        sync_errors += 1
        continue      ; read one byte at a time until sync is found
      endif
      dict.header_is_valid = 1
      dict.packet_is_valid = 0
    endif

    if ~dict.packet_is_valid then begin
      sz = dict.fifo[4]*256L + dict.fifo[5]
      nb = sz * 2
      dict.n2read = nb
      dict.packet_is_valid = 1
      continue            ; continue to read the rest of the packet
    endif


    if sync_errors ne 0 then begin
      dprint,verbose=self.verbose,dlevel=3,sync_errors,' sync errors'
    endif

    ; if it reaches this point then a valid message header+payload has been read in

    gsehdr  =  dict.fifo[0:5]
    payload =  dict.fifo[6: nb+6 - 1]
    
    self.handle,dict.fifo    ; process each packet


    if keyword_set(dict.flag) && debug(3,self.verbose,msg='status') then begin
      dprint,verbose=self.verbose,dlevel=3,header
      ;dprint,'gsehdr: ',n_elements(gsehdr)
      ;hexprint,gsehdr
      ;dprint,'payload: ',n_elements(payload)
      ;hexprint,payload
      dprint,'fifo: ', n_elements(dict.fifo)
      ;hexprint,dict.fifo
      dprint
    endif

    dict.fifo = !null

  endwhile

  if sync_errors ne 0 then begin
    dprint,verbose=self.verbose,dlevel=2,sync_errors,' GSEMSG sync errors at "'+time_string(source_dict.time_received)+'"'
    ;printdat,source
    ;hexprint,source
  endif


  if 0 then begin
    nextfile:
    dprint,!error_state.msg
    dprint,'Skipping file'
  endif

  if nbytes ne 0 then msg += string(/print,nbytes,format='(i6 ," bytes: ")')  $
  else msg+= ' No data available'

  dprint,verbose=self.verbose,dlevel=3,msg
  source_dict.msg = msg

  ;    dprint,dlevel=2,'Compression: ',float(fp)/fi.size

end



pro packet_reader::handle,buffer   ;,source_dict=source_dict

  msg_buf = buffer
  source_dict = self.source_dict
  buf = msg_buf[6:*]

  case msg_buf[3] of
    'c1'x: begin
      ;if sz ne 'c'x then begin
      ;  dprint,dlevel=1,verbose=self.verbose,'Invalid GSE message. word size: ',sz
      ;  message,'Error',/cont
      ;endif
      if debug(3,self.verbose,msg='c1 packet') then begin
        ;dprint,nb,dlevel=3
        hexprint,buf
      endif
      raw_tlm_header = self.header_struct(buf)
      if isa(self.dyndata,'dynamicarray') then self.dyndata.append,raw_tlm_header
      source_dict.gse_header  = raw_tlm_header
    end
    'c3'x: begin
      ;sync_pattern = ['1a'x,  'cf'x ,'fc'x, '1d'x ]
      ;source_dict.sync_pattern = sync_pattern
      if debug(4,self.verbose) then begin
        dprint,sz*2,dlevel=4,verbose=self.verbose
        hexprint,buf
      endif
      if ~source_dict.haskey('ccsds_dict') then begin
        source_dict.ccsds_dict =  dictionary()
        ccsds_dict = source_dict.ccsds_dict
        ccsds_dict.sync_ccsds_buf = !null
        ccsds_dict.sync_pattern = byte(['1a'x,  'cf'x ,'fc'x, '1d'x ])
      endif
      ccsds_dict = source_dict.ccsds_dict
      ccsds_dict.sync_ccsds_buf = [ccsds_dict.sync_ccsds_buf, buf]
      while 1 do begin ; start processing packet stream
        nbuf = n_elements(ccsds_dict.sync_ccsds_buf)
        skipped = 0UL
        while (nbuf ge 4) && (array_equal(ccsds_dict.sync_ccsds_buf[0:3] ,ccsds_dict.sync_pattern) eq 0) do begin
          dprint,dlevel=5,verbose=self.verbose, 'searching for sync pattern: ',nbuf
          ccsds_dict.sync_ccsds_buf = ccsds_dict.sync_ccsds_buf[1:*]    ; increment one byte at a time looking for sync pattern
          nbuf = n_elements(ccsds_dict.sync_ccsds_buf)
          skipped++
        endwhile
        if skipped ne 0 then begin
          dprint,verbose=self.verbose,dlevel=2,'Skipped ',skipped,' bytes to find sync word'
        endif
        nbuf = n_elements(ccsds_dict.sync_ccsds_buf)
        if nbuf lt 10 then begin
          dprint,verbose=self.verbose,dlevel=4,'Incomplete packet header - wait for later'
          ;         ccsds_dict.sync_ccsds_buf = sync_ccsds_buf
          break
        endif
        pkt_size = ccsds_dict.sync_ccsds_buf[4+4] * 256u + ccsds_dict.sync_ccsds_buf[5+4] + 7
        ;dprint,dlevel=2,'pkt_size: ',pkt_size
        if nbuf lt pkt_size + 4 then begin
          dprint,verbose=self.verbose,dlevel=4,'Incomplete packet - wait for later',nbuf, ' of ',pkt_size
          ;      ccsds_dict.sync_ccsds_buf = sync_ccsds_buf
          break
        endif
        ccsds_buf = ccsds_dict.sync_ccsds_buf[4:pkt_size+4-1]  ; not robust!!!
        if self.run_proc then  begin
          swfo_ccsds_spkt_handler,ccsds_buf,source_dict=ccsds_dict
        endif
        if ccsds_dict.haskey('ccsds_writer') && obj_valid(ccsds_dict.ccsds_writer) then begin   ; hook to generate ccsds files
          ccsds_writer = ccsds_dict.ccsds_writer
          ccsds_writer.directory = self.directory
          ccsds_writer.time_received = ccsds_dict.gse_header.time
          if ccsds_writer.getattr('output_lun') eq 0 then begin
            dprint,'Are you sure about this?
            ccsds_writer.output_lun = -1
            ;stop
          endif
          ccsds_writer.write,ccsds_buf
        endif
        if n_elements(ccsds_dict.sync_ccsds_buf) eq pkt_size+4 then begin
          ccsds_dict.sync_ccsds_buf = !null
        endif else begin
          ccsds_dict.sync_ccsds_buf = ccsds_dict.sync_ccsds_buf[pkt_size+4:*]
        endelse
      endwhile
    end
    else:    message,'GSE raw_tlm error - unknown code'
  endcase


end



PRO packet_reader__define
  void = {packet_reader, $
    inherits socket_reader, $    ; superclass
    ;ccsds_reader:   obj_new(), $         ; user definable object  not used
    ;gsemsg_reader:  obj_new(),  $
    sync_size:  0 , $
    header_size:  0  $
}
END