class ADCBoard: def __init__(self,b_id): self.board_id = b_id # Get position of DAQ main directory from PADME_DAQ_DIR environment variable # Default to current dir if not set self.daq_dir = os.getenv('PADME_DAQ_DIR',".") # Define id file for passwordless ssh command execution self.ssh_id_file = "%s/.ssh/id_rsa_daq"%os.getenv('HOME',"~") self.db = PadmeDB() self.status = "idle" self.re_dbinfo_line = re.compile("^\s*DBINFO\s.*$") self.set_default_config() def set_default_config(self): self.node_id = 0 self.node_ip = "" self.conet2_link = -1 self.conet2_slot = -1 self.executable = os.getenv('PADME',".")+"/PadmeDAQ/PadmeDAQ.exe" self.run_number = 0 self.proc_daq_id = -1 self.proc_zsup_id = -1 self.process_mode = "DAQ" self.config_file_daq = "unset" self.log_file_daq = "unset" self.lock_file_daq = "unset" self.initok_file_daq = "unset" self.initfail_file_daq = "unset" self.output_mode_daq = "STREAM" self.output_stream_daq = "unset" self.data_dir_daq = "unset" self.data_file_daq = "daq" self.config_file_zsup = "unset" self.log_file_zsup = "unset" self.lock_file_zsup = "unset" self.initok_file_zsup = "unset" self.initfail_file_zsup = "unset" self.input_stream_zsup = "unset" self.output_mode_zsup = "STREAM" self.output_stream_zsup = "unset" self.data_dir_zsup = "unset" self.data_file_zsup = "zsup" self.start_file = "unset" self.quit_file = "unset" self.total_daq_time = 0 # Default V1742 ADC settings self.startdaq_mode = 0 self.trigger_mode = 1 self.trigger_iolevel = "NIM" self.group_enable_mask = int('0xf',0) self.channel_enable_mask = int('0xffffffff',0) self.offset_global = int('0x5600',0) self.offset_ch = [] for ch in range(32): self.offset_ch.append(self.offset_global) self.post_trigger_size = 65 self.max_num_events_blt = 128 self.drs4_sampfreq = 2 self.drs4corr_enable = 1 # Autopass parameters (threshold in counts, duration in ns) self.auto_threshold = int('0x0400',0) self.auto_duration = 150 # Default zero suppression settings self.zero_suppression = 0 self.zs1_head = 80 self.zs1_tail = 30 self.zs1_nsigma = 3. self.zs1_nabovethr = 4 self.zs1_badrmsthr = 15. self.zs2_tail = 30 self.zs2_minrms = 4.6 self.zs2_minrms_ch = [] for ch in range(32): self.zs2_minrms_ch.append(self.zs2_minrms) # Default file parameters self.file_max_duration = 3600 self.file_max_size = 1024*1024*1024 self.file_max_events = 1000*1000 # Default DAQ control parameters self.daq_loop_delay = 10000 self.debug_scale = 100 def read_setup(self,setup): if (self.board_id == -1): print "ADCBoard - ERROR: board id not set while reading setup" return # Define regular expressions used in file parsing re_empty = re.compile("^\s*$") re_comment = re.compile("^\s*#") re_param = re.compile("^\s*(\w+)\s+(.+?)\s*$") re_chvalue = re.compile("^\s*(\d+)\s+(\w+)\s*$") # Read default board configuration from file setup_file = self.daq_dir+"/setup/"+setup+"/board_%02d.cfg"%self.board_id if (not os.path.isfile(setup_file)): print "ADCBoard - WARNING: setup file %s not found for board %d"%(setup_file,self.board_id) return f = open(setup_file) for l in f: if (re_empty.search(l) or re_comment.search(l)): continue m = re_param.search(l) if (m): (p_name,p_value) = m.group(1,2) if (p_name == "startdaq_mode"): self.startdaq_mode = int(p_value,0) elif (p_name == "trigger_mode"): self.trigger_mode = int(p_value,0) elif (p_name == "trigger_iolevel"): self.trigger_iolevel = p_value elif (p_name == "group_enable_mask"): self.group_enable_mask = int(p_value,0) elif (p_name == "channel_enable_mask"): self.channel_enable_mask = int(p_value,0) elif (p_name == "offset_global"): self.offset_global = int(p_value,0) for ch in range(0,32): self.offset_ch[ch] = self.offset_global elif (p_name == "offset_ch"): mm = re_chvalue.search(p_value) if (mm): (ch,offset) = mm.group(1,2) self.offset_ch[int(ch,0)] = int(offset,0) else: print "ADCBoard - ERROR decoding channel offset parameter in line:" print l elif (p_name == "post_trigger_size"): self.post_trigger_size = int(p_value,0) elif (p_name == "max_num_events_blt"): self.max_num_events_blt = int(p_value,0) elif (p_name == "drs4corr_enable"): self.drs4corr_enable = int(p_value,0) elif (p_name == "drs4_sampfreq"): self.drs4_sampfreq = int(p_value,0) elif (p_name == "daq_loop_delay"): self.daq_loop_delay = int(p_value,0) elif (p_name == "debug_scale"): self.debug_scale = int(p_value,0) elif (p_name == "file_max_duration"): self.file_max_duration = int(p_value,0) elif (p_name == "file_max_size"): self.file_max_size = int(p_value,0) elif (p_name == "file_max_events"): self.file_max_events = int(p_value,0) elif (p_name == "zero_suppression"): self.zero_suppression = int(p_value,0) elif (p_name == "zs1_head"): self.zs1_head = int(p_value,0) elif (p_name == "zs1_tail"): self.zs1_tail = int(p_value,0) elif (p_name == "zs1_nsigma"): self.zs1_nsigma = float(p_value) elif (p_name == "zs1_nabovethr"): self.zs1_nabovethr = int(p_value,0) elif (p_name == "zs2_tail"): self.zs2_tail = int(p_value,0) elif (p_name == "zs2_minrms"): self.zs2_minrms = float(p_value) for ch in range(32): self.zs2_minrms_ch[ch] = self.zs2_minrms elif (p_name == "zs2_minrms_ch"): mm = re_chvalue.search(p_value) if (mm): (ch,minrms) = mm.group(1,2) self.zs2_minrms_ch[int(ch,0)] = int(minrms,0) else: print "ADCBoard - ERROR decoding channel zs2_minrms parameter in line:" print l elif (p_name == "auto_threshold"): self.auto_threshold = int(p_value,0) elif (p_name == "auto_duration"): self.auto_duration = int(p_value,0) else: print "ADCBoard - WARNNING: unknown parameter found while reading default config file: %s"%p_name else: print "ADCBoard WARNING: unknown line format found while reading default config file" print l f.close() def config_list_daq(self): cfg_list = [] cfg_list.append(["daq_dir", self.daq_dir]) cfg_list.append(["ssh_id_file", self.ssh_id_file]) cfg_list.append(["executable", self.executable]) cfg_list.append(["start_file", self.start_file]) cfg_list.append(["quit_file", self.quit_file]) cfg_list.append(["run_number", str(self.run_number)]) cfg_list.append(["board_id", str(self.board_id)]) cfg_list.append(["process_mode", self.process_mode]) #if (self.run_number): # cfg_list.append(["process_id", str(self.proc_daq_id)]) cfg_list.append(["node_id", str(self.node_id)]) cfg_list.append(["node_ip", self.node_ip]) cfg_list.append(["conet2_link", str(self.conet2_link)]) cfg_list.append(["conet2_slot", str(self.conet2_slot)]) cfg_list.append(["config_file", self.config_file_daq]) cfg_list.append(["log_file", self.log_file_daq]) cfg_list.append(["lock_file", self.lock_file_daq]) cfg_list.append(["initok_file", self.initok_file_daq]) cfg_list.append(["initfail_file", self.initfail_file_daq]) cfg_list.append(["output_mode", self.output_mode_daq]) if self.output_mode_daq == "STREAM": cfg_list.append(["output_stream", self.output_stream_daq]) elif self.output_mode_daq == "FILE": cfg_list.append(["data_dir", self.data_dir_daq]) cfg_list.append(["data_file", self.data_file_daq]) cfg_list.append(["file_max_duration",str(self.file_max_duration)]) cfg_list.append(["file_max_size", str(self.file_max_size)]) cfg_list.append(["file_max_events", str(self.file_max_events)]) cfg_list.append(["total_daq_time", str(self.total_daq_time)]) cfg_list.append(["startdaq_mode", str(self.startdaq_mode)]) cfg_list.append(["trigger_mode", str(self.trigger_mode)]) cfg_list.append(["trigger_iolevel", self.trigger_iolevel]) cfg_list.append(["group_enable_mask", "%#1.1x"%self.group_enable_mask]) cfg_list.append(["channel_enable_mask", "%#8.8x"%self.channel_enable_mask]) cfg_list.append(["offset_global", "%#4.4x"%self.offset_global]) for ch in range(32): if (self.offset_ch[ch] != self.offset_global): cfg_list.append(["offset_ch", "%2d %#4.4x"%(ch,self.offset_ch[ch])]) cfg_list.append(["post_trigger_size", str(self.post_trigger_size)]) cfg_list.append(["max_num_events_blt", str(self.max_num_events_blt)]) cfg_list.append(["drs4corr_enable", str(self.drs4corr_enable)]) cfg_list.append(["drs4_sampfreq", str(self.drs4_sampfreq)]) cfg_list.append(["auto_threshold", "%#4.4x"%self.auto_threshold]) cfg_list.append(["auto_duration", str(self.auto_duration)]) cfg_list.append(["daq_loop_delay", str(self.daq_loop_delay)]) cfg_list.append(["debug_scale", str(self.debug_scale)]) return cfg_list def config_list_zsup(self): cfg_list = [] cfg_list.append(["daq_dir", self.daq_dir]) cfg_list.append(["ssh_id_file", self.ssh_id_file]) cfg_list.append(["executable", self.executable]) cfg_list.append(["run_number", str(self.run_number)]) cfg_list.append(["board_id", str(self.board_id)]) cfg_list.append(["process_mode", "ZSUP"]) #if (self.run_number): # cfg_list.append(["process_id", str(self.proc_zsup_id)]) cfg_list.append(["node_id", str(self.node_id)]) cfg_list.append(["node_ip", self.node_ip]) cfg_list.append(["config_file", self.config_file_zsup]) cfg_list.append(["log_file", self.log_file_zsup]) cfg_list.append(["lock_file", self.lock_file_zsup]) cfg_list.append(["initok_file", self.initok_file_zsup]) cfg_list.append(["initfail_file", self.initfail_file_zsup]) cfg_list.append(["output_mode", self.output_mode_zsup]) if self.output_mode_zsup == "STREAM": cfg_list.append(["output_stream", self.output_stream_zsup]) elif self.output_mode_zsup == "FILE": cfg_list.append(["data_dir", self.data_dir_zsup]) cfg_list.append(["data_file", self.data_file_zsup]) cfg_list.append(["file_max_duration", str(self.file_max_duration)]) cfg_list.append(["file_max_size", str(self.file_max_size)]) cfg_list.append(["file_max_events", str(self.file_max_events)]) cfg_list.append(["input_stream", self.input_stream_zsup]) cfg_list.append(["zero_suppression", str(self.zero_suppression)]) if (self.zero_suppression%100 == 1): cfg_list.append(["zs1_head", str(self.zs1_head)]) cfg_list.append(["zs1_tail", str(self.zs1_tail)]) cfg_list.append(["zs1_nsigma", str(self.zs1_nsigma)]) cfg_list.append(["zs1_nabovethr", str(self.zs1_nabovethr)]) cfg_list.append(["zs1_badrmsthr", str(self.zs1_badrmsthr)]) elif (self.zero_suppression%100 == 2): cfg_list.append(["zs2_tail", str(self.zs2_tail)]) cfg_list.append(["zs2_minrms", str(self.zs2_minrms)]) for ch in range(32): if (self.zs2_minrms_ch[ch] != self.zs2_minrms): cfg_list.append(["zs2_minrms_ch","%2d %f"%(ch,self.zs2_minrms_ch[ch])]) cfg_list.append(["debug_scale", str(self.debug_scale)]) return cfg_list def format_config_daq(self): cfgstring = "" for cfg in self.config_list_daq(): cfgstring += "%-30s %s\n"%(cfg[0],cfg[1]) return cfgstring def format_config_zsup(self): cfgstring = "" for cfg in self.config_list_zsup(): cfgstring += "%-30s %s\n"%(cfg[0],cfg[1]) return cfgstring def write_config(self): if self.config_file_daq == "unset": print "ADCBoard - ERROR: write_config() called but config_file_daq not set!" return f = open(self.config_file_daq,"w") f.write(self.format_config_daq()) f.close() if self.config_file_zsup == "unset": print "ADCBoard - ERROR: write_config() called but config_file_zsup not set!" return f = open(self.config_file_zsup,"w") f.write(self.format_config_zsup()) f.close() def print_config(self): print self.format_config_daq() print self.format_config_zsup() def create_proc_daq(self): # Create DAQ process in DB self.proc_daq_id = self.db.create_daq_process(self.run_number,self.node_id) if self.proc_daq_id == -1: print "ADCBoard::create_proc_daq - ERROR: unable to create new DAQ process in DB" return "error" # Add info about optical link self.db.add_daq_process_optical_link(self.proc_daq_id,self.node_id,self.conet2_link,self.conet2_slot) # Add all configuration parameters for cfg in self.config_list_daq(): self.db.add_cfg_para_proc(self.proc_daq_id,cfg[0],cfg[1]) return "ok" def create_proc_zsup(self): # Create ZSUP process in DB self.proc_zsup_id = self.db.create_zsup_process(self.run_number,self.node_id) if self.proc_zsup_id == -1: print "ADCBoard::create_proc_zsup - ERROR: unable to create new ZSUP proces in DB" return "error" # Add all configuration parameters for cfg in self.config_list_zsup(): self.db.add_cfg_para_proc(self.proc_zsup_id,cfg[0],cfg[1]) return "ok" #def get_link_id(self): # # # Convert PadmeDAQ link description to link id from DB # if (self.node_id == -1 or self.conet2_link == -1 or self.conet2_slot == -1): return -1 # return self.db.get_link_id(self.node_id,self.conet2_link/8,self.conet2_link%8,self.conet2_slot) def start_daq(self): command = "%s -c %s"%(self.executable,self.config_file_daq) # If DAQ process runs on a remote node then start it using passwordless ssh connection if self.node_id != 0: command = "ssh -i %s %s '( %s )'"%(self.ssh_id_file,self.node_ip,command) print "- Start DAQ process for board %d"%self.board_id print command print " Log written to %s"%self.log_file_daq # Open log file self.log_handle_daq = open(self.log_file_daq,"w") # Start DAQ process try: self.process_daq = subprocess.Popen(shlex.split(command),stdout=self.log_handle_daq,stderr=subprocess.STDOUT,bufsize=1) except OSError as e: print "ADCBoard::start_daq - ERROR: Execution failed: %s",e return 0 # Tag start of process in DB if self.run_number: self.db.set_process_time_create(self.proc_daq_id,self.db.now_str()) # Return process id return self.process_daq.pid def stop_daq(self): # Wait up to 5 seconds for DAQ to stop of its own (on quit file or on time elapsed) for i in range(10): if self.process_daq.poll() != None: # Process exited: clean up defunct process and close log file self.process_daq.wait() self.log_handle_daq.close() if self.run_number: self.db.set_process_time_end(self.proc_daq_id,self.db.now_str()) return True time.sleep(0.5) # Process did not stop: try sending and interrupt print "ADCBoard::stop_daq- WARNING: DAQ process did not stop on its own. Sending interrupt" if self.node_id == 0: # If process is on local host, just send a kill signal command = "kill %d"%self.process_daq.pid else: # If it is on a remote host, use ssh to send kill command. # PID on remote host is recovered from the lock file command = "ssh -i %s %s '( kill `cat %s` )'"%(self.ssh_id_file,self.node_ip,self.lock_file_daq) print command os.system(command) # Wait up to 5 seconds for DAQ to stop on interrupt for i in range(10): if self.process_daq.poll() != None: # Process exited: clean up defunct process and close log file self.process_daq.wait() self.log_handle_daq.close() if self.run_number: self.db.set_process_time_end(self.proc_daq_id,self.db.now_str()) return True time.sleep(0.5) # Process did not stop smoothly: terminate it print "ADCBoard::stop_daq - WARNING: DAQ process did not stop on interrupt. Terminating it" self.process_daq.terminate() time.sleep(1) if self.process_daq.poll() != None: self.process_daq.wait() self.log_handle_daq.close() if self.run_number: self.db.set_process_time_end(self.proc_daq_id,self.db.now_str()) return False def start_zsup(self): command = "%s -c %s"%(self.executable,self.config_file_zsup) # If ZSUP process runs on a remote node then start it using passwordless ssh connection if self.node_id != 0: command = "ssh -i %s %s '( %s )'"%(self.ssh_id_file,self.node_ip,command) print "- Start ZSUP process for board %d"%self.board_id print command print " Log written to %s"%self.log_file_zsup # Open log file self.log_handle_zsup = open(self.log_file_zsup,"w") # Start ZSUP process try: self.process_zsup = subprocess.Popen(shlex.split(command),stdout=self.log_handle_zsup,stderr=subprocess.STDOUT,bufsize=1) except OSError as e: print "ADCBoard::start_zsup - ERROR: Execution failed: %s",e return 0 # Tag start of process in DB if self.run_number: self.db.set_process_time_create(self.proc_zsup_id,self.db.now_str()) # Return process id return self.process_zsup.pid def stop_zsup(self): # Tag stop process in DB # Wait up to 5 seconds for ZSUP to stop for i in range(10): if self.process_zsup.poll() != None: # Process exited: clean up defunct process and close log file self.process_zsup.wait() self.log_handle_zsup.close() if self.run_number: self.db.set_process_time_end(self.proc_zsup_id,self.db.now_str()) return True time.sleep(0.5) # Process did not stop smoothly: terminate it print "ADCBoard::stop_zsup - WARNING: ZSUP process did not stop on interrupt. Terminating it" self.process_zsup.terminate() time.sleep(1) if self.process_zsup.poll() != None: self.process_zsup.wait() self.log_handle_zsup.close() if self.run_number: self.db.set_process_time_end(self.proc_zsup_id,self.db.now_str()) return False def parse_log_daq(self): # Look for DBINFO lines in log file and send them to DBINFO line processor if os.path.exists(self.log_file_daq) and os.path.isfile(self.log_file_daq): with open(self.log_file_daq,"r") as log: for line in log: if self.re_dbinfo_line.match(line): self.db.manage_dbinfo_entry(self.proc_daq_id,line) else: print "ADCBoard::parse_log_daq - WARNING: DAQ log file %s not found"%self.log_file_daq def parse_log_zsup(self): # Look for DBINFO lines in log file and send them to DBINFO line processor if os.path.exists(self.log_file_zsup) and os.path.isfile(self.log_file_zsup): with open(self.log_file_zsup,"r") as log: for line in log: if self.re_dbinfo_line.match(line): self.db.manage_dbinfo_entry(self.proc_zsup_id,line) else: print "ADCBoard::parse_log_zsup - WARNING: ZSUP log file %s not found"%self.log_file_zsup