/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: Pst3202.java
 * Written by Ajanta Chakraborty, Sun Microsystems.
 *
 * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.tool.simulation.test;

/**
 * Low-level control of Instek PST 3202 power supply. You
 * should instantiate {@link Pst3202Channel} instead of this class.
 * That will allow easier switching between
 * power supplies.
 */
class Pst3202 extends Equipment {

    String s = new String("null");

    /** Resolution of each channel in Volts */
    private static final float[] VOLTAGE_RESOLUTION = { 0.01f, 0.01f, 0.002f };

    /** Number of channels (value = 3) */
    public static final int NUM_CHANNELS = 3;

    /** Delay after each command, in seconds */
    public static final float GPIB_DELAY = 0.2f;
    
    /** Maximum number of errors to print out before stopping */
    public static final int MAX_NUM_ERRORS = 20;

    /**
     * Creates a new instance of power suppy. Clears the error and other status
     * registers.
     */
    public Pst3202(String name) {
        super(name);
        logInit("  Initializing Pst3202 " + name);
        try { Thread.sleep((int)(1000*GPIB_DELAY)); } catch (InterruptedException e) { }
        writeAndWait(":*CLS");
        //        System.out.println("Error4: " + readError());
        //System.out.println("Error5: " + readError());
        testConnection();

    }

    /**
     * Returns string indicating state of one of the channels
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public String getState(int channel) {
        checkChannel(channel);
        String result;
        result = "Set " + getVoltageSetpoint(channel) + " V, "
                + getCurrentSetpoint(channel) + " A\n";
        result += "Got " + readVoltage(channel) + " V, " + readCurrent(channel)
                + " A";
        return result;
    }

    static void checkChannel(int channel) {
        if (channel < 1 || channel > NUM_CHANNELS) {
            throw new IllegalArgumentException("Channel " + channel
                    + " outside allowed range 1.." + NUM_CHANNELS);
        }
    }

    private void writeAndWait(String data) {
        super.write(data);
        try { Thread.sleep((int)(1000*GPIB_DELAY)); } catch (InterruptedException e) { }
    }
    
    public void write(String data) {
        writeAndWait(data);
    }
        
    /**
     * Returns voltage resolution of channel
     * 
     * @param channel
     *            Supply channel number
     * @return Voltage resoluton of channel
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public static float getVoltageResolution(int channel) {
        checkChannel(channel);
        return VOLTAGE_RESOLUTION[channel - 1];
    }

    void testConnection() {
        writeAndWait("*idn?");
        s = read(200).trim();
        //s = s.substring(0,s.length()-1);
        System.out.println("idn " + s);
    } //end testConnection

    String readError() {
        write("SYST:ERR?");
        s = read(200);
        return s;
    }

    void handleError() {
        if (isDisabled())
            return;
        String error;
        for (int ind = 0; ind < MAX_NUM_ERRORS; ind++) {
            error = readError();
            if (error.length() <= 0) {
                System.err
                        .println("Pst3202.handleError() no reply from supply");
            }
            System.err.println("PST 3202 reports error " + error);
            if (error.equals("0, \"No error\""))
                return;
        }
        Infrastructure.nonfatal("Printed " + MAX_NUM_ERRORS
                + " errors from PST 3202");
    }

    /**
     * Measures the voltage on one of the 3 channels
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public float readVoltage(int channel) {
        checkChannel(channel);
        write(":CHAN" + channel + ":MEAS:VOLT ?");
        s = read(200);
        if (s.length() <= 0) {
            handleError();
            return -1.0f;
        }
        return Float.parseFloat(s);
    }

    /**
     * Reads back the voltage setpoint from one of the 3 channels
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public float getVoltageSetpoint(int channel) {
        checkChannel(channel);
        logOther("Reading voltage setpoint on Pst3202 " + getName()
                + ", channel " + channel);
        write(":CHAN" + channel + ":VOLT?");
        s = read(200).trim();
        if (s.length() <= 0) {
            handleError();
            return -1.0f;
        }
        return Float.parseFloat(s);
    }

    /**
     * Changes the voltage setpoint on channel <code>channel</code> to
     * <code>setVolts</code> Volts.
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public boolean setVoltage(int channel, float setVolts) {
        checkChannel(channel);
        writeAndWait(":CHAN" + channel + ":VOLT " + setVolts);
        return true;
    }

    /**
     * Changes the voltage setpoint on channel <code>channel</code> to
     * <code>setVolts</code> Volts. Verifies that it the setpoint was changed
     * successfully. Bonus points: the delay for verification ensures that a
     * rapid sequence of invocations do not overwhelm the PST 3202's command
     * buffer.
     * <p>
     * I don't expect anybody to actually use this method, I'm just keeping this
     * code around in case we need it later.
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public boolean setVoltageCheck(int channel, float setVolts) {
        checkChannel(channel);
        write(":CHAN" + channel + ":VOLT " + setVolts + ";VOLT?");
        s = read(200);
        if (s.length() <= 0) {
            handleError();
            return false;
        }
        float volts = Float.parseFloat(s);
        if (Math.abs(volts - setVolts) > 1.0001 * getVoltageResolution(channel)) {
            Infrastructure
                    .nonfatal(this + ": requested voltage setpoint is"
                            + setVolts + " V, but setpoint obtained is "
                            + volts + " V");
            return false;
        }
        return true;
    }

    /**
     * Measures the current on one of the 3 channels
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public float readCurrent(int channel) {
        checkChannel(channel);
        write(":CHAN" + channel + ":MEAS:CURR ?");
        String s = read(10);
        if (s.length() <= 0) {
            handleError();
            return -1.0f;
        }
        return Float.parseFloat(s);
    }

    /**
     * Reads back the current setpoint from one of the three channels
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public float getCurrentSetpoint(int channel) {
        checkChannel(channel);
        write(":CHAN" + channel + ":CURR ?");
        String s = read(10);
        if (s.length() <= 0) {
            handleError();
            return -1.0f;
        }
        return Float.parseFloat(s);
    }
    
    /**
     * Clear internal or device functions of the device. Among other things,
     * this should clear device GPIB error conditions and allow its use again.
     * For some devices, it appears that the clear does not work if it is too
     * soon after the error occurs.
     */
    public void clear() {
        super.clear();
        try { Thread.sleep((int)(1000*GPIB_DELAY)); } catch (InterruptedException e) { }
    }


    /**
     * Changes the current setpoint on channel <code>channel</code> to
     * <code>setAmps</code> Amps.
     * 
     * @throws IllegalArgumentException
     *             if channel not in range 1..3
     */
    public void setCurrent(int channel, float setAmps) {
        checkChannel(channel);
        writeAndWait(":CHAN" + channel + ":CURR " + setAmps);
    }

    public void switchOnOff(int state) {
        write(":OUTPUT:STATE " + state);
        try { Thread.sleep((int)(1000*GPIB_DELAY)); } catch (InterruptedException e) { }
        write(":OUTPUT:STATE?");
        s = read(200).trim();
        System.out.println("state " + s);
    }

    /**
     * Unit test verifies that voltage setpoint resolution is 0.01 V. Should
     * disconnect supply from any chips before running.
     */
    public static void main(String args[]) {
        Infrastructure.gpibControllers = new int[] { 1 };
        Logger.setLogInits(true);
        Pst3202 supply = new Pst3202("hPst3202");
        //Infrastructure.readln("Hit return to clear device:");
        supply.clear();
        //        Infrastructure.readln("Cleared");
        System.out.println("Error1: " + supply.readError());
        System.out.println("Error2: " + supply.readError());
        System.out.println("Error3: " + supply.readError());
        System.out.println("Error4: " + supply.readError());
        System.out.println("Error5: " + supply.readError());
        supply.testConnection();

        float voltageSetpoint = supply.getVoltageSetpoint(1);
        System.out.println(supply.getState(1));
        if (voltageSetpoint < 0.2f) {
            voltageSetpoint = 0.2f;
        }

        for (int chan = 1; chan <= 3; chan++) {
            System.out.println("Chan " + chan + " resolution: "
                    + Pst3202.getVoltageResolution(chan) + " V");
        }

        // Demonstrate that resolution is 0.01 V
        for (float dv = 0.f; dv < 0.014; dv += 0.002) {
            supply.setVoltage(1, voltageSetpoint - dv);
            System.out.println((voltageSetpoint - dv) + ": "
                    + supply.getState(1) + "\n");
        }

        // See if supply can tolerate rapid voltage setting
        for (float volts = 1.8f; volts >= 1.60f; volts -= 0.01f) {
            System.out.println(volts);
            supply.setVoltage(1, volts);
            supply.setVoltage(3, volts);
        }

        System.out.println("Error6: " + supply.readError());

        System.out.println(supply.getState(1));

        supply.setVoltage(1, voltageSetpoint);
        supply.setVoltage(3, voltageSetpoint);

        System.out.println("Error7: " + supply.readError());

        System.out.println(supply.getState(1));
        System.out.println("Error8: " + supply.readError());
    }

} //end class
