/**********************************************************************************************
 File      : bp2595demo.c
 Function  : voltage ramp demo for CPS model BP2595 power supply
 Platform  : Windows console app
 Author    : Jim Lamberson
 Copyright : (C) CPS, All Rights Reserved.

 This file 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.

 This file 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 file.
 If not, see <http://www.gnu.org/licenses/>.
*************************************************************************************************/

#include <stdio.h>          // printf
#include <windows.h>

#include "apiBP2595.h"      // BP2595 API definitions

void rampHv(int devaddr, int targ_v, int step_v, int step_ms, int show_meters);     // Forward declaration

/////////////////////////////////////////////////////////////////////////////////////
// Main function -- Ramp HV output to +10 kV at 1 kV/s.
// Arguments not used.

int main(int argc, char *argv[])
{
    int devaddr = 0;        // Address of BP2595 we want to talk to
    int devflags;           // List of detected devices (one bit per device)
    BP2595_ERR errcode;     // Value returned from API function
    BP2595_STATUS status;   // BP2595 status info (not used)

    errcode = BP2595_OpenApi(&devflags);        // Open the API and detect all BP2595 systems.
    if (errcode != BP2595_ERR_OK) {
        printf("Error: BP2595_OpenApi() returned %d\n", errcode);
        return 0;
    }

    if ((devflags & (1 << devaddr)) == 0) {     // Abort if the BP2595 of interest is not detected.
        printf("Error: BP2595 not detected at address %d\n", devaddr);
        return 0;
    }

    errcode = BP2595_OpenDevice(devaddr, &status);  // Open the BP2595 and fetch its status (ignored).
    if (errcode == BP2595_ERR_OK) {

        rampHv(devaddr,         // Ramp HV output from its currently programmed value to +10 kV at 1 kV/s rate (step 200 V every 200 ms):
            10000,              //   ending voltage = +10 kV
            200,                //   voltage step size = 200 V
            200,                //   step interval = 200 ms
            0);                 //   don't show meter data.

        BP2595_CloseDevice(devaddr);    // All done -- close the BP2595.
    }
    else {
        printf("Error: %s\n", BP2595_ErrString(errcode));

    }
    BP2595_CloseApi();                  // Close the API.

    return 0;
}

//////////////////////////////////////////////////////////////////////////////////////
// Ramp the HV output.
// This function uses periodic BP2595 meter reports to pace the voltage ramp.
// This method provides more accurate timing than Sleep() because it doesn't
// accumulate timing errors (due to variable execution time of API functions).
// Arguments:
//  devaddr     - device address
//  targ_v      - ending voltage upon ramp completion
//  step_v      - voltage change per ramp step
//  step_ms     - number of milliseconds per ramp step
//  show_meters - boolean: true = display meters every step; false = don't display meters

void rampHv(int devaddr, int targ_v, int step_v, int step_ms, int show_meters)
{
    int setpoint;               // Programmed output voltage
    int ramp_up;                // Boolean: true = ramp up; false = ramp down
    BP2595_ERR errcode;         // Error code returned from API functions
    BP2595_METERS meters;       // Meter data
    BP2595_STATUS status;       // Status info
    
    errcode = BP2595_GetStatusUpdate(devaddr, &status, 0);      // Read BP2595 operating mode and setpoint (use tmax=0 to avoid blocking).
    switch (errcode) {
        case BP2595_ERR_OK:         // status changed since previous status read
        case BP2595_ERR_TIMEOUT:    // status unchanged since previous status read
            break;
        default:                    // error reading status
            printf("BP2595_GetStatusUpdate() error: %s\n", BP2595_ErrString(errcode));
            return;
    }

    if ((status.StatusSystem.Live & STATE_REMOTE) == 0) {       // Abort if BP2595 in local control mode (i.e., operated via front panel).
        printf("error: BP2595 remote control is disabled\n");
        return;
    }

    if (status.StatusSystem.Live & STATE_HVEN) {                // If HV output is enabled
        setpoint = status.SetpointV;                                // Start ramp at currently programmed output voltage.
    }
    else {                                                      // Else

        setpoint = 0;                                               // Start ramp at 0 V.

        if (status.SetpointV != 0) {                                // If output is not currently programmed to 0 V
            errcode = BP2595_ProgramSetpoint(devaddr, setpoint);    //   program it to 0 V before enabling HV out.
            if (errcode != BP2595_ERR_OK) {
                printf("BP2595_ProgramSetpoint() error: %s\n", BP2595_ErrString(errcode));
                return;
            }
        }

        errcode = BP2595_ProgramHvEnable(devaddr, 1);               // Enable HV output.
        if (errcode != BP2595_ERR_OK) {
            printf("Can't enable HV output: %s\n", BP2595_ErrString(errcode));
            return;
        }
    }

    errcode = BP2595_SetMeterInterval(devaddr, step_ms);            // Start the meter stream running; this tells the BP2595 to report meter data every step_ms milliseconds.
    if (errcode != BP2595_ERR_OK) {
        printf("Can't start the meter stream: %s\n", BP2595_ErrString(errcode));
        return;
    }

    ramp_up = (targ_v > setpoint);      // calculate ramp direction

    if (step_v < 0)                     // step size = step_v magnitude
        step_v = -step_v;

    while (setpoint != targ_v)       // Ramp the HV output -----------------------------------------------------------
    {
        errcode = BP2595_GetMeterUpdate(devaddr,    // Block until next meter report, which signals that it's time for a ramp step:
            &meters,                                //   receive meter data in this buffer
            step_ms + 500);                         //   allow an extra 500 ms margin before timing out, in case the system is very busy

        if (errcode != BP2595_ERR_OK) {             // Abort if we didn't receive a meter report (due to timeout or some other error).
            printf("BP2595_GetMeterUpdate() error: %s\n", BP2595_ErrString(errcode));
            break;
        }

        if (show_meters)                            // If caller wants us to display meter data for every ramp step
            printf("setpoint = %d; meters = %d V, %d uA\n", setpoint, meters.outV, meters.outUA);   //   display output voltage and current meters.

        if (ramp_up) {                              // Bump setpoint for next step.
            setpoint += step_v;
            if (setpoint > targ_v)
                setpoint = targ_v;
        }
        else {
            setpoint -= step_v;
            if (setpoint < targ_v)
                setpoint = targ_v;
        }

        errcode = BP2595_ProgramSetpoint(devaddr, setpoint);        // Program the output voltage to the new setpoint value.
        if (errcode != BP2595_ERR_OK) {
            printf("BP2595_ProgramSetpoint() error: %s\n", BP2595_ErrString(errcode));
            break;
        }
    }

    BP2595_SetMeterInterval(devaddr, 0);    // Ramp has completed, so stop the meter stream.
}