File Keithley6487Driver.cpp

File List > midas_fe > power > Keithley6487Driver.cpp

Go to the documentation of this file

//

#include "Keithley6487Driver.h"

#include <thread>

Keithley6487Driver::Keithley6487Driver() {}

Keithley6487Driver::~Keithley6487Driver() {}

Keithley6487Driver::Keithley6487Driver(std::string n, EQUIPMENT_INFO* inf) : PowerDriver(n, inf) {
    std::cout << " Keithley6487 driver with " << instrumentID.size() << " channels instantiated."
              << std::endl;
}

INT Keithley6487Driver::ConnectODB() {
    InitODBArray();
    PowerDriver::ConnectODB();

    settings["port"](5025);
    settings["reply timout"](300);
    settings["min reply"](2);  // minimum reply , 2 chars , not 3 (not fully figured out why)
    settings["ESR"](0);
    settings["Max Voltage"](200);

    // Placeholder?
    if (false) {
        return FE_ERR_ODB;
    }
    return FE_SUCCESS;
}

void Keithley6487Driver::InitODBArray() {
    // Only one channel available
    midas::odb settings_array = {{"Channel Names", std::array<std::string, 1>()}};
    settings_array.connect("/Equipment/" + name + "/Settings");
}

bool Keithley6487Driver::AskPermissionToTurnOn(
    int /*channel*/) {  // extra check whether it is safe to tunr on supply;
    return true;
}

INT Keithley6487Driver::Init() {
    INT err;
    usb = settings["USB_PORT"];
    client->SetDefaultWaitTime(500);

    // Global reset if requested
    if (settings["Global Reset On FE Start"]) {
        if (client->Write("*RST\n")) {
            cm_msg(MINFO, "Init KEITHLEY 6487 supply ... ", "init global reset of %s", usb.c_str());
        } else {
            cm_msg(MERROR, "Init KEITHLEY 6487 supply ... ", "could not global reset %s",
                   usb.c_str());
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(client->GetWaitTime()));
    }

    // Cant actually beep :(
    if (!client->Write(GenerateCommand(COMMAND_TYPE::Beep, 0))) {
        cm_msg(MERROR, "Init KEITHLEY 6487 supply ... ", "could not beep %s", usb.c_str());
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(client->GetWaitTime()));

    // Channel selection not relevant for HAMEG supply to read ID
    // "-1" is a trick not to select a channel before the query
    idCode = ReadIDCode(-1, err);
    std::cout << "ID code: " << idCode << std::endl;

    std::vector<std::string> init({"*RST\n"});
    init.push_back("FUNC 'CURR'\n");
    init.push_back("SYST:ZCH ON\n");
    init.push_back("CURR:RANG 2e-9\n");
    init.push_back("INIT\n");
    init.push_back("SYST:ZCOR:STAT OFF\n");
    init.push_back("SYST:ZCOR:ACQ\n");
    init.push_back("SYST:ZCOR ON\n");
    init.push_back("CURR:RANG:AUTO ON\n");
    init.push_back("SYST:ZCH OFF\n");
    init.push_back("SOUR:VOLT:RANG 50\n");
    init.push_back("SOUR:VOLT 0\n");
    init.push_back("SOUR:VOLT:ILIM 2.5e-3\n");
    // SOUR:VOLT:STAT ON

    for (std::string com : init) {
        if (client->Write(com)) {
            cm_msg(MINFO, "Keithley6487Driver::Init()", "com = %s", com.c_str());
        } else {
            cm_msg(MERROR, "Keithley6487Driver::Init()", "com = %s", com.c_str());
        }
        std::this_thread::sleep_for(std::chrono::milliseconds(client->GetWaitTime()));
    }

    for (auto& s : ReadErrorQueue(-1, err)) {
        if (s.find("No error") == std::string::npos) {
            cm_msg(MERROR, "Init KEITHLEY 6487 supply ... ",
                   " Error from KEITHLEY 6487 supply, namely: %s", s.c_str());
        }
    }

    // KEITHLEY 6487 has ONLY 1 channel
    instrumentID = {1};
    int nChannels = instrumentID.size();
    settings["NChannels"] = nChannels;
    voltage.resize(nChannels);
    demandvoltage.resize(nChannels);
    current.resize(nChannels);
    currentlimit.resize(nChannels);
    state.resize(nChannels);
    OVPlevel.resize(nChannels);

    // client->FlushQueu();
    // Read channels
    for (int i(0); i < nChannels; i++) {
        state[i] = ReadState(i, err);
        if (err != FE_SUCCESS) {
            return err;
        }
    }

    // Push to odb
    variables["State"] = state;
    variables["Set State"] = state;

    for (int i(0); i < nChannels; i++) {
        voltage[i] = ReadVoltage(i, err);
        demandvoltage[i] = ReadSetVoltage(i, err);

        current[i] = ReadCurrent(i, err);
        try {
            if (variables["Current Limit"]) {
                currentlimit[i] = variables["Current Limit"];
            }
        } catch (...) {
            std::cout << "Current limit not in the ODB yet" << std::endl;
            currentlimit[i] = ReadCurrentLimit(i, err);
        }

        OVPlevel[i] = ReadOVPLevel(i, err);
        // OVPlevel[i] = 10;

        if (err != FE_SUCCESS) {
            return err;
        }
    }

    settings["Identification Code"] = idCode;
    settings["ESR"] = ReadESR(-1, err);
    settings["Read ESR"] = false;

    variables["Voltage"] = voltage;
    variables["Demand Voltage"] = demandvoltage;
    variables["Current"] = current;
    variables["Current Limit"] = currentlimit;
    variables["OVP Level"] = OVPlevel;
    variables["Demand OVP Level"] = OVPlevel;

    // Watch functions
    variables["Set State"].watch(
        [&](midas::odb& arg [[maybe_unused]]) { this->SetStateChanged(); });
    variables["Current Limit"].watch(
        [&](midas::odb& arg [[maybe_unused]]) { this->CurrentLimitChanged(); });
    variables["Demand Voltage"].watch(
        [&](midas::odb& arg [[maybe_unused]]) { this->DemandVoltageChanged(); });
    variables["Demand OVP Level"].watch(
        [&](midas::odb& arg [[maybe_unused]]) { this->DemandOVPLevelChanged(); });
    settings["Read ESR"].watch([&](midas::odb& arg [[maybe_unused]]) { this->ReadESRChanged(); });

    return FE_SUCCESS;
}

INT Keithley6487Driver::ReadAll() {
    INT err;
    INT err_accumulated;
    int nChannels = instrumentID.size();

    // Update local book keeping
    for (int i(0); i < nChannels; i++) {
        if (readonlythisindex >= 0 && i != readonlythisindex) {
            continue;
        }

        bool bvalue = ReadState(i, err);
        err_accumulated = err;
        err = 0;
        // Only update odb if there is a change
        if (state[i] != bvalue) {
            state[i] = bvalue;
            variables["State"][i] = bvalue;
        }

        float fvalue = ReadVoltage(i, err);
        err_accumulated = err_accumulated | err;
        if (fabs(voltage[i] - fvalue) > fabs(relevantchange * voltage[i])) {
            voltage[i] = fvalue;
            variables["Voltage"][i] = fvalue;
        }

        fvalue = ReadCurrent(i, err);
        err_accumulated = err_accumulated | err;
        if (fabs(current[i] - fvalue) > fabs(relevantchange * current[i])) {
            current[i] = fvalue;
            variables["Current"][i] = fvalue;
        }

        fvalue = ReadOVPLevel(i, err);
        err_accumulated = err_accumulated | err;
        if (fabs(OVPlevel[i] - fvalue) > fabs(relevantchange * OVPlevel[i])) {
            OVPlevel[i] = fvalue;
            variables["OVP Level"][i] = fvalue;
        }

        if (err_accumulated != FE_SUCCESS) {
            // Remove the success bit if there is any
            return err_accumulated & 0xFFFE;
        }
    }

    // for (auto& s: ReadErrorQueue(-1, err)) {
    //     cm_msg(MINFO, "Read KEITHLEY 6487 supply ... ", "Error queue: %s", s.c_str());
    //     if (s.find("No error") == std::string::npos) {
    //         cm_msg(MERROR, "power_fe", " Error from KEITHLEY 6487 supply : %s", s.c_str());
    //     }
    // } // Something is off here

    ClearBuffer();
    return FE_SUCCESS;
}

void Keithley6487Driver::ReadESRChanged() {
    INT err;
    if (settings["Read ESR"]) {
        settings["ESR"] = ReadESR(-1, err);
        settings["Read ESR"] = false;
    }
}

std::string Keithley6487Driver::GenerateCommand(COMMAND_TYPE cmdt, float val) {
    if (cmdt == COMMAND_TYPE::CLearStatus) {
        return "*CLS\n";
    } else if (cmdt == COMMAND_TYPE::OPC) {
        return "*OPC?\n";
    } else if (cmdt == COMMAND_TYPE::ReadESR) {
        return "*ESR?\n";
    } else if (cmdt == COMMAND_TYPE::Reset) {
        return "*RST\n";
    } else if (cmdt == COMMAND_TYPE::SelectChannel) {
        return "";  // Only one channel
    } else if (cmdt == COMMAND_TYPE::SetCurrent) {
        return "";  // Does not set the current
    } else if (cmdt == COMMAND_TYPE::ReadCurrent) {
        // return ":READ?\n";
        return "MEAS:CURR?\n";
    } else if (cmdt == COMMAND_TYPE::ReadState) {
        return "SOUR:VOLT:STAT?\n";
    } else if (cmdt == COMMAND_TYPE::SetCurrentLimit) {
        return "SOUR:VOLT:ILIM " + std::to_string(val) + "\n";
    } else if (cmdt == COMMAND_TYPE::ReadVoltage) {
        return "SOUR:VOLT?\n";  // Copy of the ReadSetVoltage!!!
    } else if (cmdt == COMMAND_TYPE::ReadSetVoltage) {
        return "SOUR:VOLT?\n";
    } else if (cmdt == COMMAND_TYPE::ReadCurrentLimit) {
        return "SOUR:VOLT:ILIM?\n";
    } else if (cmdt == COMMAND_TYPE::SetVoltage) {
        return "SOUR:VOLT " + std::to_string(val) + "\n";
    } else if (cmdt == COMMAND_TYPE::Beep) {
        cm_msg(MINFO, "KEITHLEY 6487 supply ... ", "BEEEEEEP BEEEEP mfs");
        return "\n";  // No other possibility to perform a beep :)
    } else if (cmdt == COMMAND_TYPE::SetState) {
        int ch = (int)val;
        if (ch == 1) {
            return "SOUR:VOLT:STAT ON\n";
        } else if (ch == 0) {
            return "SOUR:VOLT:STAT OFF\n";
        } else {
            cm_msg(MERROR, "KEITHLEY 6487 supply ... ", "SetState can be only 1 or 0, and not %d",
                   ch);
            return "\n";
        }
    } else if (cmdt == COMMAND_TYPE::ReadErrorQueue) {
        return "SYST:ERR:NEXT?\n";
    } else if (cmdt == COMMAND_TYPE::ReadOVPLevel) {
        return "SOUR:VOLT:RANG?\n";
    } else if (cmdt == COMMAND_TYPE::SetOVPLevel) {
        return "SOUR:VOLT:RANG " + std::to_string(val) + "\n";
    } else if (cmdt == COMMAND_TYPE::ClearBuffer) {
        return "TRAC:CLEAR\n";
    }

    return "";
}

std::string Keithley6487Driver::GenerateCommand(COMMAND_TYPE cmdt, int, float val) {
    if (cmdt == COMMAND_TYPE::SelectChannelAndSetVoltage) {
        return "SOUR:VOLT " + std::to_string(val) + "\n";
    } else {
        cm_msg(MERROR, "KEITHLEY 6487 supply ... ",
               "GenerateCommand function is usable only with SelectChannelAndSetVoltage!");
        return "";
    }
}

std::string Keithley6487Driver::ReadIDCode(int index, INT& error) {
    std::string id_code(PowerDriver::ReadIDCode(index, error));

    return id_code.substr(0, id_code.find("C10") - 1);
}