// ============================================================================ // // = CONTEXT // TANGO Project - DDC KeithleyElectrometer Support Library // // = FILENAME // Keithley_486.cpp // // = AUTHOR // X. Elattaoui // // ============================================================================ // ============================================================================ // DEPENDENCIES // ============================================================================ #include <iostream> #include <stdexcept> #include <sstream> #include <string> #include <math.h> //- for ceil #include <helpers/XString.h> #include "Keithley_486.h" #include "KeithleyDDCProtocol.h" /* * Valid Range values for a K_486 */ static const std::string K486_rangeValue[] = {"AUTO ON","2E-9","2E-8","2E-7","2E-6","2E-5","2E-4","2E-3", "AUTO OFF"}; /* * Range limit : no range for values R8 & R9 (R10 = AUTORANGE OFF) */ static short K486_rangeLimit = 8; /* * Trigger Mode limit */ static short K486_triggerModeLimit = 7; /* * Trigger Mode limit (millisec) */ static int K486_conversionRate = 360; // ============================================================================ // Keithley_486::Keithley_486 // ============================================================================ Keithley_486::Keithley_486 (std::string& comLink_device_name):AbstractElectrometerClass(comLink_device_name) { std::cout << "Keithley_486::Keithley_486 <-" << std::endl; _size = -1; _trigMod = -1; std::cout << "Keithley_486::Keithley_486 ->" << std::endl; } // ============================================================================ // Keithley_486::~Keithley_486 // ============================================================================ Keithley_486::~Keithley_486 (void) { std::cout << "Keithley_486::~Keithley_486 <-" << std::endl; std::cout << "Keithley_486::~Keithley_486 ->" << std::endl; } // ============================================================================ // Keithley_486::init_protocol // ============================================================================ bool Keithley_486::init_protocol (void) { std::string description(""); bool success = false; try { //- build the keithley Electrometer protocol obj _electrometerProtocol = new KeithleyDDCProtocol (_device_proxy_name); if(_electrometerProtocol) success = _electrometerProtocol->build_communicationLink(); } catch(Tango::DevFailed& df) { description = "FAILED to create proxy on : " + _device_proxy_name; Tango::Except::re_throw_exception (df, (const char*)"COMMUNICATION_ERROR", description.c_str(), (const char*)"Keithley_486::init_protocol"); } catch(...) { description = "FAILED to create proxy on : " + _device_proxy_name + ". Caught [...]."; Tango::Except::throw_exception ( (const char*)"COMMUNICATION_ERROR", description.c_str(), (const char*)"Keithley_486::init_protocol"); } return success; } // ============================================================================ // Keithley_486::autoRange_off // ============================================================================ void Keithley_486::autoRange_off (void) { //- send the appropriate command KeithleyDDCProtocol* _kddc = dynamic_cast<KeithleyDDCProtocol*>(_electrometerProtocol); if(_kddc) _kddc->autoRange_OFF_forK486_487(); } // ============================================================================ // Keithley_486::range_up // ============================================================================ void Keithley_486::range_up (void) { std::stringstream cmd_to_send(""); // force read of range on instrument to update _range variable this->get_configuration(); _range += 1; if(_range > K486_rangeLimit) { _range =K486_rangeLimit; throw electrometer::ElectrometerException("OUT_OF_RANGE", "Range up limit reached.", "Keithley_486::range_up( )."); } //- build and send the command cmd_to_send << _range << std::endl; _electrometerProtocol->set_range(cmd_to_send.str()); } // ============================================================================ // Keithley_486::range_down // ============================================================================ void Keithley_486::range_down (void) { std::stringstream cmd_to_send(""); // force read of range on instrument to update _range variable this->get_configuration(); _range -= 1; if(_range < 0) { _range = 0; throw electrometer::ElectrometerException("OUT_OF_RANGE", "Range down limit reached.", "Keithley_486::range_down( )."); } //- build and send the command cmd_to_send << _range << std::endl; _electrometerProtocol->set_range(cmd_to_send.str()); } // ============================================================================ // Keithley_486::get_ElectroMeterRange // ============================================================================ std::string Keithley_486::get_ElectroMeterRange (void) { // force read of range on instrument to update _range variable this->get_configuration(); return _rangeStr; } // ============================================================================ // Keithley_486::set_buffer_size() // ============================================================================ void Keithley_486::set_buffer_size (short size) { std::stringstream cmd_to_send(""); //- check if size is valid if(size<0 || size>512) throw electrometer::ElectrometerException("OUT_OF_RANGE", "Buffer size value invalid. Please enter a value in the range 0 - 512.", "Keithley_487::set_buffer_size( )."); //- just for internal use _size = size; //- send command : size = number of triggers cmd_to_send << "N" << size << "X" << std::endl; _electrometerProtocol->set_buffer_size(cmd_to_send.str()); } // ============================================================================ // Keithley_487::set_triggerMode // ============================================================================ void Keithley_486::set_triggerMode (short trigMod) { if(trigMod<0 || trigMod>K486_triggerModeLimit) throw electrometer::ElectrometerException("OUT_OF_RANGE", "Trigger mode value invalid. Please enter a value in the range 0 - 7.", "Keithley_486::set_triggerMode( )."); std::stringstream cmd_to_send(""); //- just for internal use _trigMod = trigMod; cmd_to_send << "T" << trigMod << "X" << std::endl; _electrometerProtocol->set_triggerMode(cmd_to_send.str()); } // ============================================================================ // Keithley_487::set_integrationTime // ============================================================================ void Keithley_486::set_integrationTime (double seconds) { double msSeconds = seconds / 1000; //- _size set in init_keithley() _size = (int)ceil(msSeconds / K486_conversionRate); } // ============================================================================ // Keithley_486::init_keithley : command to perform an integration cycle // ============================================================================ void Keithley_486::init_keithley (void) { //- default conversion rate // _electrometerProtocol->set_conversionRate( ); //- set default integration period // _electrometerProtocol->enable_integrationPeriod( ); //- set trigger mode this->set_triggerMode(_trigMod); //- default conversion rate _electrometerProtocol->set_conversionRate( ); //- set buffer size this->set_buffer_size(_size); //- enable readings from device buffer _electrometerProtocol->enable_readingsFromBuffer_K486_487( ); //- force readings with no prefix _electrometerProtocol->disable_readingWithPrefix(); //- enable SRQ on buffer full _electrometerProtocol->enable_SRQBufferFull (); } // ============================================================================ // Keithley_486::electrometer_status // ============================================================================ std::string Keithley_486::electrometer_status (void) { std::string kconfig("undefined configuration"); std::string argout(""); std::string tmp(""); try { kconfig = _electrometerProtocol->get_raw_status(); std::string modelNum = kconfig.substr(0,3); if(modelNum.find("486") == std::string::npos) { set_electroState(ALARM); argout = "Invalid error status string received"; return argout; } //- IDDC Error : Set when an illegal device dependent command (IDDC) such as HlX is received ("H" is illegal). tmp = kconfig.substr(3,1); short iddc = XString<short>::convertFromString(tmp); if(iddc) { argout += "IDDC error : illegal device dependent command received.\n"; } //- IDDCO Error : Set when an illegal device-dependent command option (IDDCO) such as T9X is received ("9" is illegal). tmp = kconfig.substr(4,1); short iddco = XString<short>::convertFromString(tmp); if(iddco) { argout += "IDDCO error : an illegal device-dependent command option received.\n"; } //- Remote Error : Set when a programming command is received when REN is false. tmp = kconfig.substr(5,1); short remote = XString<short>::convertFromString(tmp); if(remote) { argout += "REMOTE error : programming command is received when REN is false.\n"; } //- Self-Test Error : Set when a self-test failure (RAM and/or ROM) occurs. tmp = kconfig.substr(6,1); short selfT = XString<short>::convertFromString(tmp); if(selfT) { argout += "SELF-TEST error : Set when a self-test failure (RAM and/or ROM) occurs.\n"; } //- Trigger Overrun Error : Set when a trigger is received when the instrument is still processing a reading from a previous trigger. tmp = kconfig.substr(7,1); short trigg = XString<short>::convertFromString(tmp); if(trigg) { argout += "Trigger error : Trigger received while instrument is still processing a reading from a previous trigger.\n"; } //- Conflict Error : Set when trying to send a calibration value with the instrument on a measurement //- range that is too small to accommodate the value.. tmp = kconfig.substr(8,1); short conflict = XString<short>::convertFromString(tmp); if(conflict) { argout += "CONFLICT error : Calibration value with the instrument on a measurement range that is too small.\n"; } //- CAL LOCKED Error : Set when calibrating the instrument with the calibration switch in the locked (disabled) position. tmp = kconfig.substr(9,1); short calL = XString<short>::convertFromString(tmp); if(calL) { argout += "CAL LOCKED error : Set when calibrating the instrument with the calibration switch in the locked (disabled) position..\n"; } //- Zero Check Error : Set when trying to calibrate the instrument with zero check enabled. tmp = kconfig.substr(10,1); short zchk = XString<short>::convertFromString(tmp); if(zchk) { argout += "ZERO CHECK error : Set when trying to calibrate the instrument with zero check enabled.\n"; } //- Calibration Error : CALIBRATION - Set when calibration results in a cal constant value that is not within allowable //- limits. Repeated failure may indicate that the Model 486/487 is defective. See service information //- in this manual. tmp = kconfig.substr(11,1); short calib = XString<short>::convertFromString(tmp); if(calib) { argout += "CALIBRATION error : Set when calibration results in a cal constant value that is not within allowable limits.\n"; } //- E2PROM DEFAULTS Error : Set when power-up checksum test on defaults fail. tmp = kconfig.substr(12,1); short e2prom = XString<short>::convertFromString(tmp); if(e2prom) { argout += "E2PROM DEFAULTS error : Set when power-up checksum test on defaults fail.\n"; } //- E2pROM CAL CONSTANTS Error : Set when power-up checksum test on cal constants fail. tmp = kconfig.substr(13,1); short e2prcalL = XString<short>::convertFromString(tmp); if(e2prcalL) { argout += "E2pROM CAL CONSTANTS error : Set when power-up checksum test on cal constants fail.\n"; } //- V-SOURCE CONFLICT Error : Set when trying to send a voltage source value to the Mode1 487 that //- exceeds the maxim um limit of the currently selected voltage sauce range. On the Model 486, //- this bit is always reset to "0". tmp = kconfig.substr(14,1); short vsconf = XString<short>::convertFromString(tmp); if(vsconf) { argout += "V-SOURCE CONFLICT error : On the Model 486 this bit is always reset to \"0\".\n"; } //- V-SOURCE Error : On the Model 486, this bit is always reset to "0". tmp = kconfig.substr(15,1); short vsrc = XString<short>::convertFromString(tmp); if(vsrc) { argout += "V-SOURCE error : On the Model 486, this bit is always reset to \"0\".\n"; } if( argout.empty() ) argout = "No error."; argout = "Keithley Type " + modelNum + " Error Status :\n" + argout; } catch(...) { set_electroState(ALARM); throw electrometer::ElectrometerException("UNKNOWN_ERROR", "Cannot extract device error status.", "Keithley_486::electrometer_status( )."); } set_electroState(ON); return argout; } // ============================================================================ // Keithley_486::get_configuration // ============================================================================ std::string Keithley_486::get_configuration (void) { std::string _kstatus("undefined status"); std::string argout("undefined status"); std::string tmp(""); //- read keithley status from HW _kstatus = _electrometerProtocol->get_DDC_configuration(); //- build status try { //- model number : std::string modelNum = _kstatus.substr(0,3); //- if not expected data (here model number) if(modelNum.find("486") == std::string::npos) { set_electroState(ALARM); argout = "Invalid status string received"; return argout; } argout = "Keithley Type : " + modelNum + "\n"; //- Display Intensity ('A') tmp = _kstatus.substr(_kstatus.find('A')+1,1); short dintensity = XString<short>::convertFromString(tmp); if(!dintensity) argout += "Display Intensity : NORMAL\n"; else if(dintensity == 1) argout += "Display Intensity : DIM\n"; else if(dintensity == 2) argout += "Display Intensity : OFF\n"; //- Reading Source ('B') tmp = _kstatus.substr(_kstatus.find('B')+1,1); short rSource = XString<short>::convertFromString(tmp); if(!rSource) argout += "Reading Source : A/D Reading\n"; else if(rSource == 1) argout += "Reading Source : One Data Store Reading\n"; else if(rSource == 2) argout += "Reading Source : All Data Store Reading\n"; else if(rSource == 3) argout += "Reading Source : Max Data Store Reading\n"; else if(rSource == 4) argout += "Reading Source : Min Data Store Reading\n"; //- Zero check state ('C') tmp = _kstatus.substr(_kstatus.find('C')+1,1); if(XString<short>::convertFromString(tmp)) argout += "Zero Check : Enabled\n"; else argout += "Zero Check : Disabled\n"; //- Data Format ('G') tmp = _kstatus.substr(_kstatus.find('G')+1,1); short dformat = XString<short>::convertFromString(tmp); if(!dformat) argout += "Data Format : Reading With Prefix (ASCII)\n"; else if(dformat == 1) argout += "Data Format : Reading Without Prefix (ASCII)\n"; else if(dformat == 2) argout += "Data Format : Reading and Buffer Location With Prefix (ASCII)\n"; else if(dformat == 3) argout += "Data Format : Reading and Buffer Location Without Prefix (ASCII)\n"; else if(dformat == 4) argout += "Data Format : Binary Reading - precision, bytes reversed for Intel CPUs\n"; else if(dformat == 5) argout += "Data Format : Binary Reading - precision, bytes in normal order for Motorola CPUs\n"; else if(dformat == 6) argout += "Data Format : Binary Reading - counts and exponent, bytes reversed for Intel CPUs\n"; else if(dformat == 7) argout += "Data Format : Binary Reading - counts and exponent, bytes in normal order for Motorola CPUs\n"; //- Self Test ('J') tmp = _kstatus.substr(_kstatus.find('J')+1,1); short stest = XString<short>::convertFromString(tmp); if(!stest) argout += "Self Test : No Errors\n"; else if(stest == 1) argout += "Self Test : ROM Error\n"; else if(stest == 2) argout += "Self Test : RAM Error\n"; else if(stest == 3) argout += "Self Test : ROM and RAM Error\n"; //- EOI & Bus Hold Off ('K') tmp = _kstatus.substr(_kstatus.find('J')+1,1); short ebhoff = XString<short>::convertFromString(tmp); if(!ebhoff) argout += "EOI & Bus Hold Off : EOI and Hold-Off\n"; else if(ebhoff == 1) argout += "EOI & Bus Hold Off : No EOI and Hold-Off\n"; else if(ebhoff == 2) argout += "EOI & Bus Hold Off : EOI and no Hold-Off\n"; else if(ebhoff == 3) argout += "EOI & Bus Hold Off : No EOI and no Hold-Off\n"; //- SRQ ('M') tmp = _kstatus.substr(_kstatus.find('M')+1,3); short srqInfo = XString<short>::convertFromString(tmp); if(!srqInfo) argout += "SRQ : Disabled\n"; else if(srqInfo == 1) argout += "SRQ : Reading Overflow\n"; else if(srqInfo == 2) argout += "SRQ : Data Store Full\n"; else if(srqInfo == 4) argout += "SRQ : Data Store 1/2 full\n"; else if(srqInfo == 8) argout += "SRQ : Reading Done\n"; else if(srqInfo == 16) argout += "SRQ : Ready\n"; else if(srqInfo == 32) argout += "SRQ : Error\n"; else if(srqInfo == 128) argout += "SRQ : Voltage Source Error\n"; //- Data Store Size ('N') tmp = _kstatus.substr(_kstatus.find('N')+1,3); if(!XString<short>::convertFromString(tmp)) argout += "Data Store Size : Wrap Around\n"; else argout += "Data Store Size : " + tmp + "\n"; //- Filters ('P') tmp = _kstatus.substr(_kstatus.find('P')+1,1); short filters = XString<short>::convertFromString(tmp); if(!filters) argout += "Filters : Both Filters Disabled\n"; else if(filters == 1) argout += "Filters : Digital Filter Enabled, Analog Filter Disabled\n"; else if(filters == 2) argout += "Filters : Digital Filter Disabled, Analog Filter Enabled\n"; else if(filters == 3) argout += "Filters : Both Filters Enabled\n"; //- Range ('Rmn') //- check AutoRange tmp = _kstatus.substr(_kstatus.find('R')+1,2); if(tmp[0] == '0') argout += "Range : AutoRange Disabled\n"; else argout += "Range : AutoRange Enabled\n"; //- range value _range = atoi(&tmp[1]); _rangeStr = K486_rangeValue[_range]; argout += _rangeStr + "\n"; //- Integration Period ('S') tmp = _kstatus.substr(_kstatus.find('S')+1,1); if(!XString<short>::convertFromString(tmp)) argout += "Integration Period : Fast (4-1/2d)\n"; else argout += "Integration Period : Line Cycle (5-1/2d)\n"; //- Trigger ('T') tmp = _kstatus.substr(_kstatus.find('T')+1,1); short trigg = XString<short>::convertFromString(tmp); if(!trigg) argout += "Trigger : Multiple On Talk\n"; else if(trigg == 1) argout += "Trigger : One-shot On Talk\n"; else if(trigg == 2) argout += "Trigger : Multiple On Get\n"; else if(trigg == 3) argout += "Trigger : One-shot On Get)\n"; else if(trigg == 4) argout += "Trigger : Multiple On X\n"; else if(trigg == 5) argout += "Trigger : One-shot On X\n"; else if(trigg == 6) argout += "Trigger : Multiple On External Trigger\n"; else if(trigg == 7) argout += "Trigger : One-shot On External Trigger\n"; else if(trigg == 8) argout += "Trigger : Multiple On Operate\n"; else if(trigg == 9) argout += "Trigger : One-shot On Operate\n"; //- Relative ('Z') (baseline suppress) tmp = _kstatus.substr(_kstatus.find('Z')+1,1); if(!XString<short>::convertFromString(tmp)) argout += "Relative : Current Rel Disabled\n"; else argout += "Relative : Current Rel Enabled\n"; } catch(std::out_of_range) { set_electroState(ALARM); throw electrometer::ElectrometerException("OUT_OF_RANGE", "Cannot extract device status [find or substr failed !].", "Keithley_486::get_configuration( )."); } set_electroState(ON); return argout; }