
import gov.fnal.controls.acnet.AcnetError;
import gov.fnal.controls.daq.acquire.*;
import gov.fnal.controls.daq.context.CollectionContext;
import gov.fnal.controls.daq.datasource.*;
import gov.fnal.controls.daq.callback.*;
import gov.fnal.controls.daq.events.*;
import gov.fnal.controls.daq.items.*;
import gov.fnal.controls.daq.oac.*;
import gov.fnal.controls.daq.scaling.*;
import gov.fnal.controls.daq.util.*;

import java.util.*;

/*
   Example of reading and displaying Recycler BPM display buffer.
   The data comes from the front-end as an array of 32 bit words.
   The control system converts ("scales")
   this into an array of 216 doubles by the transformation:
    readings[i] = (double) ((float)raw[i])
   (This transformation is specified in the definition
    of the device in the device database).
   In order to interpret the data properly, it
   is necessary to invert this transformation.
   Furthermore, the data is not simply an array of
   int, but a structure that contains some floats
   as well. 
*/


public class RBPM implements ReadingArrayCallback {

    private static int N_BPM_PER_HOUSE = 48;

// Offsets defining elements of the structure

    private static int GLOBAL_STATUS = 0;
// Timestamp block (2 words)
    private static int GPSTIMESTAMP_OFFSET = 1;
    private static int ENABLE = 3;
    private static int MEASURE_TYPE = 4;
    private static int MEASURE_MODE = 5;
    private static int BEAM_TYPE = 6;
    private static int BEAM_MEAS = 7;
    private static int ARM_EVENT = 8;
    private static int TRIG_EVENT = 9;
    private static int PRETRIG_ENABLE = 10;
    private static int TRIG_DELAY = 11;
    private static int BUCKET_DELAY = 12;
    private static int INTENSITY_THRESHOLD = 13;
    private static int TIMEOUT = 14;
    private static int CALIBRATION_SPEC = 15;
    private static int MDAT_DELAY = 16;

    private static int EVENT_INDEX = 17;
    private static int DATA_TYPE = 18;
    private static int BEGIN_TURN = 19;
    private static int NUM_TURNS = 20;
    private static int CHANNEL = 21;
    private static int N_POSITION = 22;
    private static int POSITION = 23;
    private static int N_INTENSITY = POSITION + N_BPM_PER_HOUSE;
    private static int INTENSITY = N_INTENSITY + 1;
    private static int N_POSITION_RMS = INTENSITY + N_BPM_PER_HOUSE;
    private static int POSITION_RMS = N_POSITION_RMS + 1;
    private static int N_INTENSITY_RMS = POSITION_RMS + N_BPM_PER_HOUSE;
    private static int INTENSITY_RMS = N_INTENSITY_RMS + 1;
    private static int N_STATUS = INTENSITY_RMS + N_BPM_PER_HOUSE;
    private static int STATUSES = N_STATUS + 1;

// A lot of other stuff to ignore for now

    private static long updateTime = (long) 3000;

// A Recycler BPM
    private static String[] devices = { "R:BP1COV" };
// The length of the structure is 1068 bytes
    private static int[] lengths = { 1068 };
    private DaqJob job;

    /**
     * Constructor for RBPM
     *
     * @param devices    Array of device names to read
     *
     **/
    public RBPM(String[] devices, int[] lengths) {
        initJob(devices, lengths);
    }

    /**
     * Set up the DaqJob to get data
     *
     * @param devices        Array of device names to read
     *
     */
    private void initJob(String[] devices, int[] lengths) {
        // This creates a connection to dse04with name "ReadingTry"
        DaqUser user = new DaqUser("ReadingTry", "dse04");

        AcceleratorDevicesItem item = new AcceleratorDevicesItem();
        for (int i = 0; i < devices.length; i++) {
        	AcceleratorDevice device = new AcceleratorDevice(devices[i], AcceleratorDevice.READING, lengths[i], 0);
		item.addDevice(device);
        }
        //
        // We want to get data from the Accelerator (vs database, etc.)
        //
        DataSource from = new AcceleratorSource();
        //
	DelegatingCallbackDisposition to = new DelegatingCallbackDisposition(this);
        //
        // Request only one reading
        //
        OnceImmediateEvent event = new OnceImmediateEvent();
        DaqJobControl control = new DaqJobControl();

        //
        // Create the DaqJob with the above parameters
        // Data acquistion does not start until job.start() is called
        //
        job = new DaqJob(from, to, item, event, user, control);

    }

    /**
     * Start the DaqJob that was created in initJob
     *
     */
    public void startJob() {
        try {
            job.start();
            job.waitForSetup();
            job.waitForCompletion();
        } catch (Exception e) {
            System.out.println("whoops, job.start caught: " + e.getMessage());
        }
    }

    /**
     *  Main program
     *
     *  @param argv                Arguments, ignored in this example
     *
     */
    public static void main(String[] argv) {
        // java RBPM
	if (argv.length > 0) {
	  devices[0] = argv[0];
	}
	System.out.println("Reading device " + devices[0]);
        RBPM test = new RBPM(devices, lengths);
        test.startJob();
        System.exit(0);
    }

//
// This method is called when readings are available
// The BPM display buffer is a structure containing multiple data types.
// The ACNET communication protocol does not handle this well at all.
// In order to properly unpack the data, it is necessary to
// first invert the scaling transformation, then regroup/
// reinterpret the resulting unscaled array
//
    public void readings(WhatDaq device, int element, int error, Date timestamp, CollectionContext context, double[] readings){
	 System.out.println("");
         System.out.println ( device.getDeviceName() + " " + element + " " + error  + " length: " + readings.length);

// Create array of unscaled values
// For BPM devices, convert the doubles to floats to get the raw data
	int[] unscaled = new int[readings.length];
	 try {
	   ReadSetScaling scaler = new ReadSetScaling(devices[0], AcceleratorDevice.READING);
	   for (int ii = 0; ii < readings.length; ii++) {
	     float temp = (float) readings[ii];
// Just copy the 32 bits to an int
	     unscaled[ii] = Float.floatToIntBits(temp);
	   }
	 } catch (AcnetException e) {
	   System.out.println("Error getting scaling information " + e.getMessage());
	 }
      System.out.println("Global Status: " + Integer.toHexString(unscaled[GLOBAL_STATUS]));
// GPS time stamp
      int gps = unscaled[GPSTIMESTAMP_OFFSET];
      int gpsns = unscaled[GPSTIMESTAMP_OFFSET + 1];
      long gpsTime = ((long)gps) * 1000 + ((long) gpsns)/1000000;
      System.out.println("GPS seconds: " + Integer.toHexString(gps));
      System.out.println("GPS nsecond: " + Integer.toHexString(gpsns));
      System.out.println("GPS date: " + (new Date(gpsTime)));

      System.out.println("Enable: " + Integer.toHexString(unscaled[ENABLE]));
      System.out.println("Measure Type: " + Integer.toHexString(unscaled[MEASURE_TYPE]));
      System.out.println("Measure Mode: " + Integer.toHexString(unscaled[MEASURE_MODE]));
      System.out.println("Beam Type: " + Integer.toHexString(unscaled[BEAM_TYPE]));
      System.out.println("Beam Meas: " + Integer.toHexString(unscaled[BEAM_MEAS]));
      System.out.println("Arm Event: " + Integer.toHexString(unscaled[ARM_EVENT]));
      System.out.println("Trig Event: " + Integer.toHexString(unscaled[TRIG_EVENT]));
      System.out.println("Pretrig Enable: " + Integer.toHexString(unscaled[PRETRIG_ENABLE]));
      System.out.println("Trigger Delay: " + Integer.toHexString(unscaled[TRIG_DELAY]));
      System.out.println("Bucket Delay: " + Integer.toHexString(unscaled[BUCKET_DELAY]));
      System.out.println("Intensity Threshold: " + Float.intBitsToFloat(unscaled[INTENSITY_THRESHOLD]));
      System.out.println("Timeout: " + Integer.toHexString(unscaled[TIMEOUT]));
      System.out.println("Calibration Spec: " + Integer.toHexString(unscaled[CALIBRATION_SPEC]));
      System.out.println("MDAT Delay: " + Integer.toHexString(unscaled[MDAT_DELAY]));
      System.out.println("Event Index: " + Integer.toHexString(unscaled[EVENT_INDEX]));
      System.out.println("Data Type: " + Integer.toHexString(unscaled[DATA_TYPE]));
      System.out.println("Begin Turn: " + Integer.toHexString(unscaled[BEGIN_TURN]));
      System.out.println("Number of Turns: " + Integer.toHexString(unscaled[NUM_TURNS]));
      System.out.println("Channel: " + Integer.toHexString(unscaled[CHANNEL]));
      System.out.println("Number Positions: " + Integer.toHexString(unscaled[N_POSITION]));
      for (int jj = 0; jj < unscaled[N_POSITION]; jj++) {
	System.out.println("Position " + jj + ": " + Float.intBitsToFloat(unscaled[POSITION + jj]));
      }
      System.out.println("Number Intensities: " + Integer.toHexString(unscaled[N_INTENSITY]));
      for (int jj = 0; jj < unscaled[N_INTENSITY]; jj++) {
	System.out.println("Intensity " + jj + ": " + Float.intBitsToFloat(unscaled[INTENSITY + jj]));
      }
      System.out.println("Number Position RMS: " + Integer.toHexString(unscaled[N_POSITION_RMS]));
      for (int jj = 0; jj < unscaled[N_POSITION_RMS]; jj++) {
	System.out.println("Position RMS " + jj + ": " + Float.intBitsToFloat(unscaled[POSITION_RMS + jj]));
      }
      System.out.println("Number Intensities RMS: " + Integer.toHexString(unscaled[N_INTENSITY_RMS]));
      for (int jj = 0; jj < unscaled[N_INTENSITY_RMS]; jj++) {
	System.out.println("Intensity RMS " + jj + ": " + Float.intBitsToFloat(unscaled[INTENSITY_RMS + jj]));
      }
      System.out.println("Number Statuses: " + Integer.toHexString(unscaled[N_STATUS]));
      for (int jj = 0; jj < unscaled[N_STATUS]; jj++) {
	System.out.println("Status " + jj + ": " + Integer.toHexString(unscaled[STATUSES + jj]));
      }
      
	  
    }

}
