diff --git a/configs/lr_iviscan.json b/configs/lr_iviscan.json index 637179bbe96583d4049ab027fa9678fa6221e143..6c1a3759dc2b33e33fd6324820bb0e42f5c568cb 100644 --- a/configs/lr_iviscan.json +++ b/configs/lr_iviscan.json @@ -5,10 +5,10 @@ { "name": "HV", "initial_voltage": 0, - "final_voltage": -150, + "final_voltage": -200, "step_size": -5, - "measurements_per_step": 3, - "sleeping_time_per_step_s": 10, + "measurements_per_step": 10, + "sleeping_time_per_step_s": 2, "current_protection": 0.0001 }, "passive_channels": @@ -34,4 +34,4 @@ [ ] } -} \ No newline at end of file +} diff --git a/libDCS/iviscan.py b/libDCS/iviscan.py index e68434045f6454e84829066ed03ae0e7929a02bd..6a5ff4eff69d50ea0a13eb5b3a7fc10f28292e0f 100644 --- a/libDCS/iviscan.py +++ b/libDCS/iviscan.py @@ -6,6 +6,7 @@ import numpy as np import time import getpass from influxdb import InfluxDBClient, exceptions +import logging, coloredlogs, verboselogs # LR stuff LABREMOTE = 1 @@ -18,31 +19,14 @@ except: LABREMOTE = 0 - def sigint_handler(sig, frame): - print('[ info ][ ivi ] Received SIGINT. Exiting!') + logger.info('Received SIGINT. Exiting!') sys.exit(0) signal.signal(signal.SIGINT, sigint_handler) -def meas_voltage(hw, ps, args): - return ps.measureVoltage() - -def power_on(hw, ps, args): - if args.voltage_level and args.current_protect: - ps.setVoltageLevel(args.voltage_level) - ps.setCurrentProtect(args.current_protect) - ps.turnOn() - return 0 - - -def power_off(hw, ps, args): - ps.turnOff() - return 0 - - def powerPassiveChannels(config, key, what): if what == "turnOn": @@ -51,8 +35,7 @@ def powerPassiveChannels(config, key, what): ps = hw.getPowerSupplyChannel(elem["name"]) - print() - print("setting I to %2g A and V to %2g V on channel %s" % (elem["current"], elem["voltage"], elem["name"])) + logger.info("setting I to %2g A and V to %2g V on channel %s" % (elem["current"], elem["voltage"], elem["name"])) ps.setVoltageLevel(elem["voltage"]) ps.setCurrentLevel(elem["current"]) @@ -65,6 +48,70 @@ def powerPassiveChannels(config, key, what): ps.turnOn() +def ramp(varName, pschannel, target, step = None, tsleep = None): + + if varName == "voltage": + + xVarName = "voltage" ; xUnit = "V" + yVarName = "current" ; yUnit = "A" + measXvar = pschannel.measureVoltage + setXvar = pschannel.setVoltageLevel + measYvar = pschannel.measureCurrent + tolerance = 1 # V + if step is None: step = 5 # V + if tsleep is None: tsleep = 1 # s + + elif varName == "current": + + xVarName = "current" ; xUnit = "A" + yVarName = "voltage" ; yUnit = "V" + measXvar = pschannel.measureCurrent + setXvar = pschannel.setCurrentLevel + measYvar = pschannel.measureVoltage + tolerance = 0.02 # A + if step is None: step = 0.1 # A + if tsleep is None: tsleep = 0.5 # s + + else: + logger.error("'varName' can only be 'voltage' or 'current'") + + + # Get current values of V and I + xmeas = measXvar() + ymeas = measYvar() + #isOn = pschannel.getPowerSupply().isOn(pschannel.getChannel()) + isOn = (measXvar() != 0 or measYvar() != 0) + + diff = target - xmeas + logging.debug(varName + " difference (target - meas): " + str(diff) + " " + xUnit) + + # Decide whether ramping is finished or not + if abs(diff) <= tolerance / 2: + logger.info("ramping finished") + return xmeas + + + # determine direction of ramp + if diff < 0 and step > 0 or diff > 0 and step < 0 : + step = -step + + logger.info("ramping from %.2g %s to %.2g %s in steps of %.2g %s" % (xmeas, xUnit, target, xUnit, step, xUnit)) + + while abs(xmeas) >= abs(target)+abs(step) or abs(xmeas) <= abs(target)-abs(step): + setXvar(xmeas+step) + time.sleep(tsleep) + xmeas = measXvar() + ymeas = measYvar() + logger.debug("PSU is on? " + str(isOn) + "; " + xVarName + ": " + str(xmeas) + " " + xUnit + "; " + yVarName + ": " + str(ymeas) + " " + yUnit) + + diff = target - xmeas + + if abs(diff) < abs(step): + logging.debug("--------------------") + logger.info("[%s, %s] = [%.2g %s, %.2g %s] " % (xVarName, yVarName, xmeas, xUnit, ymeas, yUnit)) + ramp(xVarName, pschannel, target, step/5.) + + def doivi(config, w, dbSink=""): if w == "iv": @@ -83,7 +130,7 @@ def doivi(config, w, dbSink=""): ch = config[key]["target_channel"] ps = hw.getPowerSupplyChannel(ch["name"]) if ps == None: - print("[ error ][ ivi ] change the config and try again!") + logger.error("change the config and try again!") exit(1) if w == "iv": @@ -97,7 +144,7 @@ def doivi(config, w, dbSink=""): measYvar = ps.measureVoltage - print("Starting the %s scan..." % w) + logger.info("Starting the %s scan..." % w) if w == "iv": ps.setCurrentProtect(ch["current_protection"]) @@ -127,30 +174,34 @@ def doivi(config, w, dbSink=""): data[yKeyOutUnit+"_Compliance"] = config[key]["target_channel"][yVarName+"_protection"] data[outputKey] = [] - # < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < - # Output to the terminal - # > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > - colWidths = "{0:8}{1:17}{2:20}{3:20}{4:20}{5:20}" - print(colWidths.format("# time", xVarName + "_Level", xVarName + "_SenseMean", xVarName + "_SenseStd", yVarName + "_SenseMean", yVarName + "_SenseStd")) - # < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < xVar = ch["initial_" + xVarName] step = ch["step_size"] startX = measXvar() + #wasOn = ps.isOn() + wasOn = (measXvar() != 0 or measYvar() != 0) - # Go to the starting point - steps = np.linspace(startX, xVar, 10) - for e in steps: - setXvar(e) - time.sleep(1) + # Ramp to the starting point if PSU is already on + if wasOn: + ramp(xVarName, ps, xVar)#, 5, 1) + else: + setXvar(xVar) + ps.turnOn() + + # Output to the terminal + # > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > + colWidths = "{0:8}{1:17}{2:20}{3:20}{4:20}{5:20}" + print(colWidths.format("# time", xVarName + "_Level", xVarName + "_SenseMean", xVarName + "_SenseStd", yVarName + "_SenseMean", yVarName + "_SenseStd")) + # < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < while (xVar - ch["final_" + xVarName])* step/abs(step) <= 0: setXvar(xVar) - ps.turnOn() + if w == "vi": + ps.turnOn() time.sleep(ch["sleeping_time_per_step_s"]) @@ -166,7 +217,7 @@ def doivi(config, w, dbSink=""): yVarPerStep.append(measY) if measY > ch[yVarName + "_protection"]: - print("Reached " + yVarName + " protection value") + logger.warning("Reached " + yVarName + " protection value") break else: @@ -208,22 +259,25 @@ def doivi(config, w, dbSink=""): xVar += step # turn everything off (power-cycle the PS) - #ps.turnOff() + if w == "vi": + ps.turnOff() + continue break - - # Go back to the point before the scans - steps = np.linspace(measXvar(), startX, 10) - for e in steps: - setXvar(e) - time.sleep(1) - + with open(args.outfile+"_"+w+".json", "w") as f: + json.dump(data, f, indent=4) + + if w == "iv": + # ramp HV outside of IV curve according to module testing document + # 2V/s for 3D, 5V/s for planar + ramp(xVarName, ps, startX)#, 5, 1) + if not wasOn: + ps.turnOff() powerPassiveChannels(config, key, "turnOff") - with open("output_"+w+".json", "w") as f: - json.dump(data, f, indent=4) + @@ -255,14 +309,14 @@ def toInfluxDB(points, dbSink, host='localhost', port=8086): # If auth is enabled, ask the user for his/her password if "authorization failed" in str(e): - print("[ warn ] Input the password for user \"{}\" in InfluxDB.\n" + logger.warning("Input the password for user \"{}\" in InfluxDB.\n" " If you don\'t want to input the password in the future until\n" " you close your current bash session use \n" " \"export INFLUXDBPWD=yourPassword\".".format(usr)) try: pwd = getpass.getpass() - except: - print() + except Exception as e: + logger.error(e) sys.exit(1) # And connect again. @@ -279,8 +333,8 @@ def toInfluxDB(points, dbSink, host='localhost', port=8086): except exceptions.InfluxDBClientError as e: # If not, let the user know what's happening and exit. - print("[ error ] Received error from InfluxDB: {}".format(e)) - print("[ info ] Please specify the db connectivity parameters " + logger.error("Received error from InfluxDB: {}".format(e)) + logger.info("Please specify the db connectivity parameters " "\"database\", and also \"username\", " "\"host\" and \"port\" in the .\"influxdb_cfg\" section in" "the provided DCS config file. Make sure the provided username and password are correct.\n") @@ -290,11 +344,15 @@ def toInfluxDB(points, dbSink, host='localhost', port=8086): try: client.write_points(points) except exceptions.InfluxDBClientError as e: - print("[ error ] Received error from InfluxDB: {}".format(e)) + logger.error("Received error from InfluxDB: {}".format(e)) sys.exit(1) if __name__ == "__main__": + + coloredlogs.install(level="INFO") ### controls availability of colored logs + logger = verboselogs.VerboseLogger('ivi') + parser = argparse.ArgumentParser() parser.add_argument( @@ -306,8 +364,9 @@ if __name__ == "__main__": parser.add_argument( "-j", "--json", - required=True, - help="path to iviscan.json", + required=False, + default="configs/lr_iviscan.json", + help="path to iviscan.json. Default is \"configs/lr_iviscan.json\"", ) parser.add_argument( "-e", @@ -315,8 +374,23 @@ if __name__ == "__main__": required=True, help="powersupply.json Configuration file with the power supply definition", ) - + parser.add_argument( + "-o", + "--outfile", + required=False, + default="output", + help="output file name without file extension. Will be saved in .json format. Default is \"output\"", + ) + parser.add_argument( + "-u", + "--upload", + required=False, + action="store_true", + help="If set will upload data to influxDB. \"influxdb_cfg\" required in powersupply.json", + ) + args = parser.parse_args() + logger.debug(args) # - - - - - - - - - - with open(args.json) as f: @@ -339,20 +413,16 @@ if __name__ == "__main__": try: import _labRemote as labRemote except: - print("Couldn't find the labRemote Python libraries. Make sure to add them to your $PYTHONPATH first by either doing") - print() - print(" export PYTHONPATH=/path/to/labRemote/build/lib:$PYTHONPATH") - print() - print(" or writing \"/path/to/labRemote/build/lib\" in \"" + args.equip + "\"") - print() - print("And make sure also that you have previously built labRemote enabling the Python bindings (cmake3 -DUSE_PYTHON=on)") + logger.error("Couldn't find the labRemote Python libraries") + logger.info("Make sure to add them to your $PYTHONPATH first by either doing") + logger.info(" export PYTHONPATH=/path/to/labRemote/build/lib:$PYTHONPATH") + logger.info(" or writing \"/path/to/labRemote/build/lib\" in \"" + args.equip + "\"") + logger.info("Also make sure labRemote is built with Python bindings (cmake3 -DUSE_PYTHON=on)") exit(1) - - - uploadToInflux = False - if uploadToInflux: - #dbSink = psConfig["influxdb_cfg"] + + if args.upload: + dbSink = psConfig["influxdb_cfg"] pass else: dbSink = ""