diff --git a/webservice/request_darks.py b/webservice/request_darks.py index 09b33283c82d55b39862e80c91a0f3165e060078..1cafba1a183fcaa0ef36cbc81ddad82dcbd5f360 100644 --- a/webservice/request_darks.py +++ b/webservice/request_darks.py @@ -11,6 +11,9 @@ parser.add_argument('--proposal', type=str, parser.add_argument('--instrument', type=str, choices=["SPB", "MID", "FXE", "SCS", "DETLAB", "SQS", "HED"], help='The instrument') +parser.add_argument('--detectors', type=str, nargs='*', + help='A list of detectors to process, default ["all"]', + default=['all']) parser.add_argument('--cycle', type=str, help='The facility cycle') parser.add_argument('--run-high', type=str, help='Run number of high gain data as an integer') @@ -20,7 +23,8 @@ parser.add_argument('--run-low', type=str, help='Run number of low gain data as an integer') parser.add_argument('--run', type=str, help='Run number as an integer') parser.add_argument('--reservation', type=str, help='Reservation to run on, default is to use configured reservations') #noqa - +parser.add_argument('--bkg', action='store_true', + help='Background mode: exit script after requesting dark.') args = vars(parser.parse_args()) @@ -31,7 +35,7 @@ con = socket.connect("tcp://max-exfl016:5555") uuid = str(datetime.now().timestamp().as_integer_ratio()[0]) parm_list = ["dark", uuid, "SASEX", args["instrument"], args["cycle"], - args["proposal"]] + args["proposal"], ','.join(args["detectors"])] if "run_high" in args and args["run_high"]: parm_list += ["(\"run-high\", \"{}\")".format(args["run_high"])] @@ -52,6 +56,8 @@ print(resp.decode()) while True: socket.send("['query-rid', '{}']".format(uuid).encode()) resp = socket.recv_multipart()[0] + if args["bkg"]: + break rl = resp.decode().split("\n") r = ["QUEUE" if "QUEUE" in r else r for r in rl] r = " | ".join(r) diff --git a/webservice/serve_overview.py b/webservice/serve_overview.py index 559c2f9c3eae6d616e41c1a2fc21abaaf76c25a9..1dcf1120df4753cdd16a99220c925a7361ac3396 100644 --- a/webservice/serve_overview.py +++ b/webservice/serve_overview.py @@ -40,6 +40,7 @@ class RequestHandler(BaseHTTPRequestHandler): def init_config(self): global config + global cal_config self.nodes_avail_res_cmd = config["shell-commands"]["nodes-avail-res"] self.total_jobs_cmd = config["shell-commands"]["total-jobs"] @@ -92,7 +93,71 @@ class RequestHandler(BaseHTTPRequestHandler): with open(fpath, "rb") as f: self.wfile.write(f.read()) return + if "update_form?" in self.path: + pars = self.path.split("?")[1].split("&") + pars = {x.split("=")[0]: x.split("=")[1] for x in pars} + if pars['instrument'] in ['Nothing', 'none']: + self.wfile.write(bytes("<br>", "utf8")) + return + host = config["server-config"]["host"] + port = config["server-config"]["port"] + if 'request_dark' in pars: + par_list = ['python', 'request_darks.py', '--bkg'] + del pars["request_dark"] + for k, v in pars.items(): + par_list.append('--{}'.format(str(k).replace("_", "-"))) + if k == 'detectors': + for det in v.split(","): + par_list.append('{}'.format(det)) + elif k == 'proposal': + par_list.append('{:06d}'.format(int(v))) + else: + par_list.append('{}'.format(v)) + + par_list = list(filter(None, par_list)) + print('REQUEST DARK: ', par_list) + timeout = config["server-config"]["dark-timeout"] + try: + msg = check_output(par_list, shell=False, + timeout=timeout).decode('utf8') + except Exception as e: + msg = str(e) + + self.wfile.write(bytes("<br>" + str(msg), "utf8")) + return + tmpl = Template(self.templates["checkbox"]) + detectors = list(cal_config['dark'][pars['instrument']].keys()) + + det_list = [] + if 'detectors' in pars: + det_list = pars['detectors'].split(",") + + det_names = [] + for d in detectors: + if d in det_list: + det_names.append(["checked", d]) + else: + det_names.append(["", d]) + + run_names = [] + run1_det = ["FASTCCD", "EPIX", "DSSC", "PNCCD"] + run3_det = ["LPD", "AGIPD", "JUNGFRAU"] + msg = '' + if any(y in x for x in det_list for y in run1_det): + run_names = ['run'] + if any(y in x for x in det_list for y in run3_det): + if run_names == []: + run_names = ['run_high', 'run_med', 'run_low'] + else: + run_names = [] + msg = "Incompatible choice" + + message = tmpl.render(detectors=det_names, runs=run_names, + message=msg, host=host, port=port) + + self.wfile.write(bytes(message, "utf8")) + return # Send headers self.send_header('Content-type', 'text/html') self.end_headers() @@ -241,10 +306,15 @@ class RequestHandler(BaseHTTPRequestHandler): tmpl = self.templates["running-jobs"] running_jobs_r = Template(tmpl).render(running_jobs=running_jobs) + tmpl = self.templates["request-dark"] + request_dark_r = Template(tmpl).render( + instruments=cal_config['dark'].keys()) + tmpl = Template(self.templates["main-doc"]) message = tmpl.render(maxwell_status=maxwell_status_r, log_output=log_output_r, last_characterizations=last_characterizations_r, + request_dark=request_dark_r, running_jobs=running_jobs_r) # Write content as utf-8 data self.wfile.write(bytes(message, "utf8")) @@ -255,7 +325,10 @@ def run(configfile, port=8008): print('reading config file') with open(configfile, "r") as cf: global config - config = yaml.load(cf.read()) + config = yaml.load(cf.read(), Loader=yaml.FullLoader) + with open(config["web-service"]["cal-config"], "r") as cf: + global cal_config + cal_config = yaml.load(cf.read(), Loader=yaml.FullLoader) print('starting server...') sconfig = config["server-config"] server_address = (sconfig["host"], sconfig["port"]) diff --git a/webservice/serve_overview.yaml b/webservice/serve_overview.yaml index 7bd4f95bb8c2f357b11cd6a76a8785614220c581..fd2c4b49f88379d9f8a086bcdd030fffcd3f7c91 100644 --- a/webservice/serve_overview.yaml +++ b/webservice/serve_overview.yaml @@ -4,6 +4,8 @@ templates: log-output: ./templates/log_output.html last-characterizations: ./templates/last_characterizations.html running-jobs: ./templates/running_jobs.html + request-dark: ./templates/request_dark.html + checkbox: ./templates/checkbox.html css: ./templates/serve_overview.css shell-commands: @@ -43,6 +45,8 @@ run-candidates: server-config: port: 8008 host: max-exfl016 + dark-timeout: 30 web-service: - job-db: ./webservice_jobs.sqlite \ No newline at end of file + job-db: ./webservice_jobs.sqlite + cal-config: /home/karnem/myscratch/calibration3/calibration_configurations/default.yaml \ No newline at end of file diff --git a/webservice/templates/checkbox.html b/webservice/templates/checkbox.html new file mode 100644 index 0000000000000000000000000000000000000000..154128addd5ce626fed7589df1e37708469c963f --- /dev/null +++ b/webservice/templates/checkbox.html @@ -0,0 +1,11 @@ +{% for checked, detector in detectors %} + <input type="checkbox" onchange="javascript:loadCheckbox();" id="detector{{loop.index}}" + name="detector" value="{{detector}}" {{checked}}> {{detector}} +{% endfor %} + +<br> +{{message}} +{% for run_name in runs %} + <label >{{run_name}}:</label> + <input type="number" id="run{{loop.index}}" name="{{run_name}}" min="1" max="999999" size="4"> +{% endfor %} \ No newline at end of file diff --git a/webservice/templates/main_doc.html b/webservice/templates/main_doc.html index 90bb6063bc4839192756b8c14e9f9385c7393df8..1a35e3a930a05a8fe6439b070fc4d5b2bc0c9def 100644 --- a/webservice/templates/main_doc.html +++ b/webservice/templates/main_doc.html @@ -7,7 +7,7 @@ <body> {{ maxwell_status }} {{ running_jobs }} -{{ last_characterizations }} +{{ request_dark }} {{ log_output }} </body> </html> diff --git a/webservice/templates/request_dark.html b/webservice/templates/request_dark.html new file mode 100644 index 0000000000000000000000000000000000000000..c7dfefbaeca5f0e17c5561d7be3353bb739b7f18 --- /dev/null +++ b/webservice/templates/request_dark.html @@ -0,0 +1,116 @@ +<script> + +function loadCheckbox(command) { + var xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function() { + if (this.readyState == 4 && this.status == 200) { + document.getElementById("demo").innerHTML = this.responseText; + } + }; + + var cycle = document.getElementById("cycle").value; + var proposal = document.getElementById("proposal").value; + + var element = document.getElementById("detector1"); + var counter = 1 + var det_list = "" + while (typeof(element) != 'undefined' && element != null) { + if(element.checked == true){ + det_list += element.value + "," + } + counter++; + element = document.getElementById("detector"+counter); + } + + var run_element = [] + run_element[0] = document.getElementById("run1"); + counter = 1 + var run_list = "" + while (typeof(run_element[counter-1]) != 'undefined' && run_element[counter-1] != null) { + run_list += "&" + run_element[counter-1].name + "=" + run_element[counter-1].value + counter++; + run_element[counter-1] = document.getElementById("run"+counter); + } + + var e = document.getElementById("instruments"); + var msg = "instrument="+e.options[e.selectedIndex].value; + + msg += "&cycle="+cycle + msg += "&proposal="+proposal + msg += "&detectors="+det_list + msg += run_list + if (command=='request'){ + if (!isInt(cycle) || cycle.length!=6){ + document.getElementById("msg_err").innerHTML = 'Cycle is not defined' + return + } + if (!isInt(proposal) || proposal.length<4 || proposal.length>6){ + document.getElementById("msg_err").innerHTML = 'Proposal is not defined' + return + } + if (e.options[e.selectedIndex].value == 'none'){ + document.getElementById("msg_err").innerHTML = 'Instrument is not defined' + return + } + if (det_list==''){ + document.getElementById("msg_err").innerHTML = 'Detector is not defined' + return + } + for(i=0; i<counter-1; i++){ + if(!isInt(run_element[i].value) || run_element[i].value.length>4 ){ + document.getElementById("msg_err").innerHTML = run_element[i].name + ' is not defined' + return + } + for(j=i+1; j<counter-1; j++){ + if( run_element[i].value == run_element[j].value ){ + err_msg = run_element[i].name + ' and ' + run_element[j].name + ' are the same.' + document.getElementById("msg_err").innerHTML = err_msg + return + } + } + } + msg += "&request_dark=1" + document.getElementById("msg_err").innerHTML = ' <br>' + e.value = "none" + document.getElementById("demo").innerHTML = 'Requesting dark...' + } + + xhttp.open("GET", "{{host}}:{{port}}/update_form?"+msg, true); + xhttp.send(); +} + +function isInt(value) { + return !isNaN(value) && + parseInt(Number(value)) == value && + !isNaN(parseInt(value, 10)); +} +</script> + +<script type="text/javascript"> +window.addEventListener("load",function(){ + document.getElementById("instruments").value="none"; +},false); +</script> + +<div class="block"> + <h2>Request dark</h2> + + <form action="" target="result" method="get"> + <label >cycle:</label> + <input type="number" id="cycle" name="cycle" min="100000" max="999999" size="6"> + <label >proposal:</label> + <input type="number" id="proposal" name="proposal" min="1000" max="999999" size="6"> + <select id="instruments" onChange="javascript:loadCheckbox();" > + <option value ="none" selected="selected" >Nothing</option> + {% for instrument in instruments %} + <option value ="{{instrument}}"> {{instrument}} </option> + {% endfor %} + </select> + <div id="demo"> + </div> + <p><input type="button" value="request" onclick="javascript:loadCheckbox('request');" ></p> + <div id="msg_err" style="color:red;font-weight:bold;font-size:160%;" > + </div> + </form> + +</div> diff --git a/webservice/webservice.py b/webservice/webservice.py index 51e60a32a64258b696d368a733988ce1ff13e097..d9676fce9136886feec6dde03b187d86aba34f7b 100644 --- a/webservice/webservice.py +++ b/webservice/webservice.py @@ -475,8 +475,13 @@ async def server_runner(config, mode): runnr = runnr.replace("r", "") wait_runs = [runnr] if action == 'dark': - rid, sase, instrument, cycle, proposal = payload[:5] - runs = payload[5:] # can be many + (rid, sase, instrument, cycle, proposal, + det_list) = payload[:6] + msg = "Dark characterization for {} at {} is requested. " \ + "Checking files..." + logging.info(msg.format(det_list, instrument)) + det_list = det_list.split(',') + runs = payload[6:] # can be many for i, run in enumerate(runs): erun = eval(run) if isinstance(erun, (list, tuple)): @@ -581,14 +586,15 @@ async def server_runner(config, mode): for detector, dconfig in pconf[instrument].items(): # check if we find files according # to mapping in raw run folder + if detector not in det_list and det_list[0]!='all': + continue fl = glob.glob( "{}/RAW-*{}*.h5".format(rpath, dconfig["inset"])) if len(fl): thisconf = copy.copy(dconfig) - out_folder = '_'.join((out_folder, - dconfig["inset"])) thisconf["in-folder"] = in_folder - thisconf["out-folder"] = out_folder + thisconf["out-folder"] = '/'.join((out_folder, + detector.replace('-', '_'))) # don't need this for xfel-calibrate del thisconf["inset"]