Beispiel #1
0
    def Run(self):
        try:
            self.Log = LogFile(self.Args.logfile)
        except IOError:
            print "File: " + self.Log.Filename + " cannot be opened : " + str(
                sys.exc_info()[1])
            #TODO: log to stderr
            raise IOError()
        #if args.v > 0 : print "Processing Log File "  + log.Filename + ":" + str(log.Length) + " bytes"
        logline = self.Log.RetrieveCurrentLine()
        widgets = [
            'Processing potential messages: ',
            progressbar.Percentage(), ' ',
            progressbar.Bar(marker=progressbar.RotatingMarker()), ' ',
            progressbar.ETA()
        ]
        if self.Args.quiet is False:
            pbar = progressbar.ProgressBar(widgets=widgets, maxval=100).start()
        while logline != "":  #TODO: Make this actually exit on EOF
            self.IsMatch(logline)
            if self.Args.quiet is False:
                pbar.update((1.0 * self.Log.Position / self.Log.Length) * 100)
            logline = self.Log.RetrieveCurrentLine()

        if self.Args.quiet is False: pbar.finish()
Beispiel #2
0
	def log(self, msg):
		msg = 'PSA[%s@%s]: %s' % (self.Name, self.Root, msg)
		if LogFile:
			LogFile.log(msg)
		else:
			print msg
			sys.stdout.flush()
def connectToAddr():
    con = paramiko.SSHClient()
    con.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    #print(server_addr.get())
    con.connect(server_addr.get(),username=config['auth']['username'],
                password=config['auth']['password'])
    
    name = server_addr.get()
    name = name[:name.find('.')]
    connections[name]=con
    #print("name: ", name)
    tree.insert('','end',server_addr.get(),text=name,value=con,tags=('clicky','simple'))
    tree.tag_bind('clicky','<ButtonRelease-3>',itemClicked)
    #sftp = con.open_sftp()

    logs = []
    
    for log in config['logs']:
        logs.append(resolve_log(con,log))
    
    for log in logs:
        #print("log: ",log[0])
        try:
            logf = LogFile(frame,filterFrame,con,log[0],server_addr.get(),tempdir)
            log_files[logf.getName()] = logf
            parent = tree.insert(server_addr.get(),'end',server_addr.get()+log[0],text=logf.name,
                        values=logf.getName(),tags=('selected'))
            tree.tag_bind('selected','<ButtonRelease-1>',logSelected)
        except Exception as e:
            print("woopies! something went wrong with main log: ",e)
            continue
Beispiel #4
0
 def __init__(self, request, filename=None, buffer_size=65536, **kw):
     if filename == None:
         rootpagename = kw.get('rootpagename', None)
         if rootpagename:
             from MoinMoin.Page import Page
             filename = Page(request, rootpagename).getPagePath('event-log', isfile=1)
         else:
             filename = request.rootpage.getPagePath('event-log', isfile=1)
     LogFile.__init__(self, filename, buffer_size)
Beispiel #5
0
class LoPhiLogger(multiprocessing.Process):
    """
        This class will continuously read in data from a queue and write to
        the specified log file(s).
    """
    def __init__(self,
                 data_queue,
                 filename=G.DEFAULT_LOG_FILE,
                 filetype="tsv",
                 packed_data=False):
        """
            Initialize our logger to read from a queue and write to files
        """
        # Packed in protobuf or just a dict?
        self.packed_data = packed_data

        # Remember our input queue
        self.DATA_QUEUE = data_queue

        # Create our log file
        self.logfile = LogFile(filename,
                               reprint_header=False,
                               output_type=filetype)

        multiprocessing.Process.__init__(self)

    def run(self):
        """
            Loop forever consuming output from our SUA threads
        """
        # Wait for output to start returning, and handle appropriately
        while True:

            # Get our log data
            output_packed = self.DATA_QUEUE.get()

            # If its a kill command, just post it
            if output_packed == G.CTRL_CMD_KILL:
                logger.debug("Logger killed...")
                # Close our logs cleanly
                for log in self.logfiles.keys():
                    self.logfiles[log].close()
                break

            if self.packed_data:
                output = protobuf.unpack_sensor_output(output_packed)
            else:
                output = output_packed

            # Log to file
            self.logfile.append(output)

        logger.debug("Logger closed.")
Beispiel #6
0
 def __init__(self, request, filename=None, buffer_size=65536, **kw):
     if filename == None:
         rootpagename = kw.get('rootpagename', None)
         if rootpagename:
             filename = Page(request, rootpagename).getPagePath('edit-log', isfile=1)
         else:
             filename = request.rootpage.getPagePath('edit-log', isfile=1)
     LogFile.__init__(self, filename, buffer_size)
     self._NUM_FIELDS = 9
     self._usercache = {}
     
     # Used by antispam in order to show an internal name instead
     # of a confusing userid
     self.uid_override = kw.get('uid_override', None)
    def _ping(self, host):
        """
        Ping one host and set status to a Zipato sensor.

        :param str host: Target host.
        :rtype: str
        :returns: Status message

        """
        message_log = LogFile(self.MESSAGE_LOG)
        if host in self.PING_HOSTS.keys():
            ep = self.PING_HOSTS[host]['ep']
            apikey = self.PING_HOSTS[host]['apikey']
        else:
            message = (
                "ping: host={}, Host has not been configured for ping")
            message = message.format(host)
            message_log.write([message])
            return None
        try:
            for i in range(self.PING_COUNT):
                command = "{}ping -c {} {}"
                command = command.format(
                    self.PING_PATH, str(self.PING_COUNT), host)
                p = subprocess.Popen(
                    command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
                    shell=True)
                stdout, stderr = p.communicate()
                result = re.match('.*0 received.*', str(stdout), re.DOTALL)
                status = result is None
                if status:
                    break
                sleep(self.PING_INTERVAL)
            # Set the status of a Zipato sensor to the ping status
            zipato_connection = ZipatoConnection(self.ZIPATO_SERIAL)
            if status:
                zipato_connection.set_sensor_status(
                    ep=ep, apikey=apikey, status=True)
            else:
                zipato_connection.set_sensor_status(
                    ep=ep, apikey=apikey, status=False)
        except:
            error_log = LogFile(self.ERROR_LOG)
            message = 'ping: host={}'.format(host)
            error_log.write([message])
            traceback_message = traceback.format_exc()
            error_log.write([traceback_message], date_time=False)
        message = "ping: host={}, status={}".format(host, status)
        message_log.write([message])
Beispiel #8
0
class LoPhiLogger(multiprocessing.Process):
    """
        This class will continuously read in data from a queue and write to
        the specified log file(s).
    """

    def __init__(self, data_queue, filename=G.DEFAULT_LOG_FILE, filetype="tsv", packed_data=False):
        """
            Initialize our logger to read from a queue and write to files
        """
        # Packed in protobuf or just a dict?
        self.packed_data = packed_data

        # Remember our input queue
        self.DATA_QUEUE = data_queue

        # Create our log file
        self.logfile = LogFile(filename, reprint_header=False, output_type=filetype)

        multiprocessing.Process.__init__(self)

    def run(self):
        """
            Loop forever consuming output from our SUA threads
        """
        # Wait for output to start returning, and handle appropriately
        while True:

            # Get our log data
            output_packed = self.DATA_QUEUE.get()

            # If its a kill command, just post it
            if output_packed == G.CTRL_CMD_KILL:
                logger.debug("Logger killed...")
                # Close our logs cleanly
                for log in self.logfiles.keys():
                    self.logfiles[log].close()
                break

            if self.packed_data:
                output = protobuf.unpack_sensor_output(output_packed)
            else:
                output = output_packed

            # Log to file
            self.logfile.append(output)

        logger.debug("Logger closed.")
Beispiel #9
0
def connectToAddr():
    ##    con = paramiko.SSHClient()
    ##    con.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ##    #print(server_addr.get())
    ##    con.connect(server_addr.get(),username=config['auth']['username'],
    ##                password=config['auth']['password'])

    name = server_addr.get()
    name = name[:name.find('.')]
    con = connection.Connection(server_addr.get(), config['auth']['username'],
                                config['auth']['password'], name)
    connections[name] = con
    #print("name: ", name)
    tree.insert('',
                'end',
                server_addr.get(),
                text=name,
                value=('', con),
                tags=('clicky', 'simple'))
    tree.tag_bind('clicky', '<ButtonRelease-3>', itemClicked)
    #sftp = con.open_sftp()

    logs = []

    for log in config['logs']:
        logs.append(resolve_log(con, log))

    for log in logs:
        #print("log: ",log[0])
        try:
            logf = LogFile(frame, filterFrame, con, log[0], server_addr.get(),
                           tempdir)
            if not logf.checkExists():
                print("Log file doesn't exist: ", log[0])
                continue
            log_files[logf.getName()] = logf
            parent = tree.insert(server_addr.get(),
                                 'end',
                                 server_addr.get() + log[0],
                                 text=logf.name,
                                 values=(logf.lastEdit, logf.getName()),
                                 tags=('selected'))
            #tree.set(parent,'updated',)
            tree.tag_bind('selected', '<ButtonRelease-1>', logSelected)
        except Exception as e:
            print("woopies! something went wrong with main log: ", e)
            continue
Beispiel #10
0
    def __init__(self,
                 data_queue,
                 filename=G.DEFAULT_LOG_FILE,
                 filetype="tsv",
                 packed_data=False):
        """
            Initialize our logger to read from a queue and write to files
        """
        # Packed in protobuf or just a dict?
        self.packed_data = packed_data

        # Remember our input queue
        self.DATA_QUEUE = data_queue

        # Create our log file
        self.logfile = LogFile(filename,
                               reprint_header=False,
                               output_type=filetype)

        multiprocessing.Process.__init__(self)
Beispiel #11
0
    def __init__(self, data_queue, filename=G.DEFAULT_LOG_FILE, filetype="tsv", packed_data=False):
        """
            Initialize our logger to read from a queue and write to files
        """
        # Packed in protobuf or just a dict?
        self.packed_data = packed_data

        # Remember our input queue
        self.DATA_QUEUE = data_queue

        # Create our log file
        self.logfile = LogFile(filename, reprint_header=False, output_type=filetype)

        multiprocessing.Process.__init__(self)
Beispiel #12
0
class TestLogFile(unittest.TestCase):
    LOGPATH = "/tmp/log.html.gz"
    JOBPATH = "/tmp"
    JOB = "testjob"

    def setUp(self):
        self.logfile = LogFile(self.LOGPATH, self.JOBPATH, self.JOB)

    def test_mkdir_log_path(self):
        self.logfile.mkdir_job_path()
        self.assertTrue(os.path.isdir(self.logfile.jobpath))

    def test_unzip_log_file(self):
        self.logfile.mkdir_job_path()
        self.logfile.unzip_log_file()
        fname = "{}/log.html".format(self.logfile.jobpath)
        self.assertTrue(os.path.isfile(fname))

    def test_parse_log(self):
        self.logfile.parse_log(None)
Beispiel #13
0
Env.COLOR_BLUE = Env.color and "\033[1;34m" or ""
Env.COLOR_GREEN = Env.color and "\033[1;32m" or ""
Env.COLOR_YELLOW = Env.color and "\033[1;33m" or ""
Env.COLOR_RED = Env.color and "\033[1;31m" or ""
Env.COLOR_CYAN = Env.color and "\033[1;36m" or ""

Env.fcgi_cgi = which('fcgi-cgi')

Env.dir = mkdtemp(suffix='-l2-tests')
Env.defaultwww = os.path.join(Env.dir, "www", "default")

Env.log = open(os.path.join(Env.dir, "tests.log"), "w")
if Env.color: Env.log = RemoveEscapeSeq(Env.log)
if Env.debug:
    logfile = Env.log
    Env.log = LogFile(sys.stdout, **{"[log]": logfile})
    sys.stderr = LogFile(sys.stderr, **{"[stderr]": logfile})
    sys.stdout = LogFile(sys.stdout, **{"[stdout]": logfile})
else:
    Env.log = LogFile(Env.log)
    sys.stderr = LogFile(sys.stderr, **{"[stderr]": Env.log})
    sys.stdout = LogFile(sys.stdout, **{"[stdout]": Env.log})

failed = False

try:
    # run tests
    tests = Tests()
    tests.LoadTests()
    failed = True
    try:
class Xray_Beam_Stabilization(object):
    name = "xray_beam_stabilization"

    log = LogFile(name+".log",["date time","filename","x","y","x_control","y_control","image_timestamp"])
    if log.filename == "":
        log.filename = "//mx340hs/data/anfinrud_1702/Logfiles/xray_beam_stabilization.log"
    auto_update = False
    x_PV = persistent_property("x_PV","14IDC:mir2Th.VAL") # Piezo control voltage in V
    x_read_PV = persistent_property("x_read_PV","14IDC:mir2Th.RBV") 
    y_PV = persistent_property("y_PV","14IDA:DAC1_4.VAL") # Theta in mrad
    y_read_PV = persistent_property("y_read_PV","14IDA:DAC1_4.VAL") 
    x_gain = persistent_property("x_gain",0.143) # mrad/mm
    y_gain = persistent_property("y_gain",2.7) # V/mm was: 1/3.3e-3
    x_nominal = persistent_property("x_nominal",175.927) # mm from left, 2016-03-05
    y_nominal = persistent_property("y_nominal",174.121) # mm from top,  2016-03-05
    history_length = persistent_property("history_length",5) 
    average_samples = persistent_property("average_samples",5)
    x_enabled = False
    y_enabled = False
    ROI_width = persistent_property("ROI_width",1.0) # mm
    x_ROI_center = persistent_property("x_ROI_center",175.9) # mm from left
    y_ROI_center = persistent_property("y_ROI_center",174.1) # mm from top    
    min_SNR = persistent_property("min_SNR",5.0) # signal-to-noise ratio
    # Use only images matching this pattern, e.g. "5pulses"
    history_filter = persistent_property("history_filter","")     
    analysis_filter = persistent_property("analysis_filter","")     

    def __init__(self):
        """"""
        start_new_thread(self.keep_updated,())
    
    def keep_updated(self):
        while True:
            try:
                if self.auto_update: self.update()
                if self.x_enabled: self.apply_x_correction()
                if self.y_enabled: self.apply_y_correction()
            except Exception,m: info("xray_beam_stabilization: %s" % m); break
            sleep(1)

    def update(self):
        t = self.image_timestamp
        if t != 0 and abs(t - self.last_image_timestamp) >= 0.1:
            f = self.image_basename
            x,y = self.beam_position
            xc,yc = self.x_control,self.y_control
            self.log.log(f,x,y,xc,yc,t)

    @property
    def x_average(self): return average(self.x_samples)

    @property
    def y_average(self): return average(self.y_samples)

    @property
    def x_history(self): return self.history("x",count=self.history_length)

    @property
    def y_history(self): return self.history("y",count=self.history_length)

    @property
    def t_history(self): return self.history("date time",count=self.history_length)

    @property
    def x_samples(self): return self.history("x",count=self.average_samples)

    @property
    def y_samples(self): return self.history("y",count=self.average_samples)

    @property
    def last_image_timestamp(self):
        t = self.history("image_timestamp",count=1)
        t = t[0] if len(t)>0 else 0 
        return t

    def get_x_control(self): return tofloat(caget(self.x_read_PV))
    def set_x_control(self,value): return caput(self.x_PV,value)
    x_control = property(get_x_control,set_x_control)

    def get_y_control(self): return tofloat(caget(self.y_read_PV))
    def set_y_control(self,value): return caput(self.y_PV,value)
    y_control = property(get_y_control,set_y_control)

    def get_x_control_average(self): return average(self.x_control_samples)
    def set_x_control_average(self,value): self.x_control = value
    x_control_average = property(get_x_control_average,set_x_control_average)

    def get_y_control_average(self): return average(self.y_control_samples)
    def set_y_control_average(self,value): self.y_control = value
    y_control_average = property(get_y_control_average,set_y_control_average)

    @property
    def x_control_samples(self):
        return self.history("x_control",count=self.average_samples)
    
    @property
    def y_control_samples(self):
        return self.history("y_control",count=self.average_samples)

    @property
    def x_control_history(self):
        return self.history("x_control",count=self.history_length)

    @property
    def y_control_history(self):
        return self.history("y_control",count=self.history_length)

    @property
    def x_control_corrected(self):
        """Value for the y control in roder to bring the y position back to
        its nominal value"""
        x_control = self.x_control_average - \
            (self.x_average - self.x_nominal)*self.x_gain
        return x_control

    @property
    def y_control_corrected(self):
        """Value for the y control in roder to bring the y position back to
        its nominal value"""
        y_control = self.y_control_average - \
            (self.y_average - self.y_nominal)*self.y_gain
        return y_control

    def apply_correction(self):
        self.apply_x_correction()
        self.apply_y_correction()        

    def apply_x_correction(self):
        if self.image_OK:
            self.x_control = self.x_control_corrected

    def apply_y_correction(self):
        if self.image_OK:
            self.y_control = self.y_control_corrected

    @property
    def x_beam(self): return self.beam_position[0]

    @property
    def y_beam(self): return self.beam_position[1]

    @property
    def beam_position_HyunSun(self):
        from transmissive_beamstop import beam_center
        x,y = beam_center(self.image)
        return x,y

    @property
    def beam_position(self):
        xprofile,yprofile = xy_projections(self.image,self.ROI_center,self.ROI_width)
        x,y = CFWHM(xprofile),CFWHM(yprofile)
        return x,y

    @property
    def ROI_center(self): return self.x_ROI_center,self.y_ROI_center

    @property
    def image_OK(self):
        if self.image_overloaded: OK = False
        elif self.SNR < self.min_SNR: OK = False
        elif self.analysis_filter not in self.image_basename: OK = False
        else: OK = True
        return OK

    @property
    def image_overloaded(self):
        return overloaded_pixels(self.image,self.ROI_center,self.ROI_width)

    @property
    def SNR(self):
        xprofile,yprofile = xy_projections(self.image,self.ROI_center,self.ROI_width)
        return (SNR(xprofile)+SNR(yprofile))/2

    @property
    def image(self):
        from numimage import numimage
        from numpy import uint16
        filename = self.image_filename
        if filename: image = numimage(filename)
        else: image = self.default_image
        return image

    @property
    def default_image(self):
        from numimage import numimage
        from numpy import uint16
        image = numimage((3840,3840),pixelsize=0.0886,dtype=uint16)+10
        return image

    @property
    def image_timestamp(self):
        """Full pathname of the last recorded image"""
        from os.path import getmtime
        from normpath import normpath
        filename = self.image_filename
        t = getmtime(normpath(filename)) if filename else 0
        return t

    @property
    def image_filename(self):
        """Full pathname of the last recorded image"""
        image_filenames = self.image_filenames
        if len(image_filenames)>0: image_filename = image_filenames[-1]
        else: image_filename = ""
        self.last_image_filename = image_filename
        return image_filename

    last_image_filename = ""

    @property
    def image_filenames(self):
        """Full pathnames of the last recorded images"""
        from rayonix_detector_continuous import ccd
        return ccd.image_filenames
        
    @property
    def image_basename(self):
        """Filename of the last recorded image, with out directory"""
        from os.path import basename
        return basename(self.image_filename)

    def history(self,name,count):
        """Log history filtered by image filename pattern"""
        from numpy import array,chararray
        filename = self.log.history("filename",count=count)
        values = self.log.history(name,count=count)
        filename = array(filename,str).view(chararray)
        match = filename.find(self.history_filter)>=0
        values = array(values)[match]
        return values
Beispiel #15
0
class ClusterGroup(object):
    """
        A Group of word cluster, representing the unique log types within a logfile
        """

    Args = ""
    Log = ""
    VarThreshold = 10  #How many siblings a string node must have before it is considered to be variable data
    VarDistance = 20
    rootNode = ClusterNode(NodeContent="ROOTNODE")
    entries = []

    def __init__(self, args):
        self.rootNode = ClusterNode(NodeContent="ROOTNODE")
        self.Args = args

    def IsMatch(self, logline):
        '''
                Test the incoming log line to see if it matches this clustergroup
                Return boolean match
                '''
        logwords = commonvars.FindCommonRegex(logline).split()

        #TODO Split at '=' marks as well

        currentNode = self.rootNode
        for logword in logwords:  #process logs a word at a time
            #match our own children first
            match = currentNode.MatchChild(MatchContent=logword)

            if match == None:  #then try our siblings
                match = currentNode.MatchNephew(MatchContent=logword)
            if match == None:  #then add a new child
                match = currentNode.AddChild(NodeContent=logword)

            if match == None:
                print "FAILED"
            else:
                currentNode = match

    def IsEndNode(self, Node):
        '''
                Is This Node the final word of a log template?
                
                @return: True or False
                '''
        endnode = False
        hasNephews = False
        if (len(Node.Children) is
                0):  #I'm an EndNode for a log wording cluster
            if Node.Parent is not None:  #let's make sure our siblings are all endnodes too, and this is really var data
                for sibling in Node.Parent.Children:
                    if len(sibling.Children) > 0:
                        hasNephews = True
                if (hasNephews is False) and (len(
                        Node.Parent.Children) >= ClusterGroup.VarThreshold
                                              ):  #log event ends in a variable
                    endnode = True
                if (hasNephews is
                        False) and (len(Node.Parent.Children)
                                    == 1):  #log event ends in a fixed string
                    endnode = True
        if endnode is True:
            entry = Node.GeneratePath()
            if entry not in self.entries:
                self.entries.append(entry)

    def BuildResultsTree(self, node):
        '''
                Recurse through the Node Tree, identifying and printing complete log patterns'
                
                @return: None (recursive function)
                '''
        if self.IsEndNode(node) == True:
            return None  # no children so back up a level
        for childnode in node.Children:
            self.BuildResultsTree(childnode)

    def Results(self):
        '''
                Display all identified unique log event types
                
                @return None
                '''
        #if options.outfile == true: dump to file
        print "\n========== Potential Unique Log Events ==========\n"
        self.BuildResultsTree(self.rootNode)

        #Todo - commandline args to toggle levenshtein identification of dupes

        previous = ''
        for entry in self.entries:
            if levenshtein.levenshtein(entry,
                                       previous) < ClusterGroup.VarDistance:
                print "\t" + entry
            else:
                print entry
            previous = entry

    def Run(self):
        try:
            self.Log = LogFile(self.Args.logfile)
        except IOError:
            print "File: " + self.Log.Filename + " cannot be opened : " + str(
                sys.exc_info()[1])
            #TODO: log to stderr
            raise IOError()
        #if args.v > 0 : print "Processing Log File "  + log.Filename + ":" + str(log.Length) + " bytes"
        logline = self.Log.RetrieveCurrentLine()
        widgets = [
            'Processing potential messages: ',
            progressbar.Percentage(), ' ',
            progressbar.Bar(marker=progressbar.RotatingMarker()), ' ',
            progressbar.ETA()
        ]
        if self.Args.quiet is False:
            pbar = progressbar.ProgressBar(widgets=widgets, maxval=100).start()
        while logline != "":  #TODO: Make this actually exit on EOF
            self.IsMatch(logline)
            if self.Args.quiet is False:
                pbar.update((1.0 * self.Log.Position / self.Log.Length) * 100)
            logline = self.Log.RetrieveCurrentLine()

        if self.Args.quiet is False: pbar.finish()

    def GenPlugin(self):
        '''
            Create a Template OSSIM agent plugin file using the identified log templates as SIDs
            
            @return: The filename of the generated plugin
            '''
        generator = plugingenerate.Generator(self.entries)
        generator.WritePlugin()
        return generator.PluginFile
 def handle_request(self):
     """Web server function."""
     host = request.args.get('host')
     mac = request.args.get('mac')
     tab = request.args.get('tab')
     request_json = request.get_json()
     Debug.debug_print(3, "request_json: " + pprint.pformat(request_json))
     try:
         message = request.path
         if request.path == self.WEB_GUI_PATH:
             settings = self.render_settings_html()
             active_tab = 'about'
             if tab is not None:
                 active_tab = tab
             result = render_template(
                 'index.html', settings=settings, active_tab=active_tab,
                 restart_ping_path=Settings.WEB_API_PATH + 'restart_ping',
                 poweron_path=Settings.WEB_API_PATH + 'poweron',
                 poweroff_path=Settings.WEB_API_PATH + 'poweroff')
         elif request.path == self.WEB_API_PATH + 'poweron':
             message = 'poweron: mac={}'
             message = message.format(str(mac))
             result = self._poweron()
         elif request.path == self.WEB_API_PATH + 'poweroff':
             message = 'poweroff: host={}'
             message = message.format(str(host))
             result = self._poweroff()
         elif request.path == self.WEB_API_PATH + 'restart_ping':
             message = 'restart_ping'
             result = self._restart_ping()
         elif request.path == self.WEB_API_PATH + 'save_settings':
             message = 'save_settings'
             result = self._save_settings(request_json)
         elif request.path == self.WEB_API_PATH + 'delete_param_value':
             param = None
             value = None
             if 'param' in request_json:
                 param = request_json['param']
             if 'value' in request_json:
                 value = request_json['value']
             message = 'delete_param_value: param={}, value={}'
             message = message.format(param, value)
             result = self._delete_param_value(param, value)
         elif request.path == self.WEB_API_PATH + 'add_param_value':
             param = None
             if 'param' in request_json:
                 param = request_json['param']
             if 'value' in request_json:
                 value = request_json['value']
             message = 'add_param_value: param={}, value={}'
             message = message.format(param, value)
             result = self._add_param_value(param, value)
     except:
         error_log = LogFile(self.ERROR_LOG)
         error_log.write([message])
         traceback_message = traceback.format_exc()
         error_log.write([traceback_message], date_time=False)
         if Debug.debug > 0:
             Debug.debug_print(1, traceback_message)
             return self._json_response(traceback_message, 500)
         return self._json_response('Internal system error!', 500)
     message_log = LogFile(self.MESSAGE_LOG)
     message_log.write([message])
     return result
Beispiel #17
0
 def setUp(self):
     self.logfile = LogFile(self.LOGPATH, self.JOBPATH, self.JOB)
class Xray_Beam_Check(object):
    name = "xray_beam_check"

    class Settings(object):
        name = "xray_beam_check.settings"

        # X-Ray beam steering controls.
        # Horizontal deflection mirror jacks
        def get_x1_motor(self):
            return MirrorH.m1.prefix

        def set_x1_motor(self, value):
            MirrorH.m1.prefix = value

        x1_motor = property(get_x1_motor, set_x1_motor)

        def get_x2_motor(self):
            return MirrorH.m2.prefix

        def set_x2_motor(self, value):
            MirrorH.m2.prefix = value

        x2_motor = property(get_x2_motor, set_x2_motor)

        def get_y_motor(self):
            return MirrorV.prefix

        def set_y_motor(self, value):
            MirrorV.prefix = value

        y_motor = property(get_y_motor, set_y_motor)

        dx_scan = persistent_property(
            "dx_scan", 6 * 0.000416 * 2 /
            1.045)  # stepsize: 0.000416*2/1.045 = 0.000796 mrad
        dy_scan = persistent_property("dy_scan", 0.150)  # in V
        x_resolution = persistent_property(
            "x_resolution",
            0.000416 * 2 / 1.045)  # stepsize: 0.000416*2/1.045 = 0.000796 mrad
        y_resolution = persistent_property("y_resolution", 0.001)  # in V

        beamline_mode = persistent_property("beamline_mode", "SAXS/WAXS")
        beamline_modes = ["SAXS/WAXS", "Laue"]

        @property
        def x_aperture_control(self):
            """Which motor to use to narrow down the horizontal arperture"""
            return s1hg if self.beamline_mode == "Laue" else shg

        @property
        def y_aperture_control(self):
            """Which motor to use to narrow down the horizontal arperture"""
            return svg

        # To narrow down aperture upstream of the detector for higher senitivity
        def get_x_aperture_motor(self):
            return self.x_aperture_control.prefix

        def set_x_aperture_motor(self, value):
            self.x_aperture_control.prefix = value

        x_aperture_motor = property(get_x_aperture_motor, set_x_aperture_motor)

        def get_y_aperture_motor(self):
            return self.y_aperture_control.prefix

        def set_y_aperture_motor(self, value):
            self.y_aperture_control.prefix = value

        y_aperture_motor = property(get_y_aperture_motor, set_y_aperture_motor)

        x_aperture_norm = persistent_property("x_aperture_norm", 0.150)
        y_aperture_norm = persistent_property("y_aperture_norm", 0.050)
        x_aperture_scan = persistent_property("x_aperture_scan", 0.050)
        y_aperture_scan = persistent_property("y_aperture_scan", 0.020)

        def get_x_aperture(self):
            return self.x_aperture_control.command_value

        def set_x_aperture(self, value):
            self.x_aperture_control.command_value = value

        x_aperture = property(get_x_aperture, set_x_aperture)

        def get_y_aperture(self):
            return self.y_aperture_control.command_value

        def set_y_aperture(self, value):
            self.y_aperture_control.command_value = value

        y_aperture = property(get_y_aperture, set_y_aperture)

        @property
        def x_aperture_moving(self):
            return self.x_aperture_control.moving

        @property
        def y_aperture_moving(self):
            return self.y_aperture_control.moving

        def get_timing_system_ip_address(self):
            return timing_system.ip_address

        def set_timing_system_ip_address(self, value):
            timing_system.ip_address = value

        timing_system_ip_address = property(get_timing_system_ip_address,
                                            set_timing_system_ip_address)

        def get_scope_ip_address(self):
            return xray_pulse.scope.ip_address

        def set_scope_ip_address(self, value):
            xray_pulse.scope.ip_address = value

        scope_ip_address = property(get_scope_ip_address, set_scope_ip_address)

        ms_on_norm = persistent_property("ms_on_norm", True)
        xosct_on_norm = persistent_property("xosct_on_norm", True)

        scan_timeout = 30  # seconds

        timing_mode = persistent_property("timining_mode", "SAXS/WAXS")
        timing_modes = ["SAXS/WAXS", "Laue"]

        @property
        def timing_sequencer(self):
            return timing_sequencer if self.timing_mode == "Laue" \
                else Ensemble_SAXS

    settings = Settings()

    log = LogFile(name + ".log", ["date time", "x_control", "y_control"])
    if log.filename == "":
        log.filename = "//mx340hs/data/anfinrud_1703/Logfiles/xray_beam_check.log"

    x_scan_x = persistent_property("x_scan_x", [])
    x_scan_I = persistent_property("x_scan_I", [])
    x_scan_sigI = persistent_property("x_scan_sigI", [])
    y_scan_y = persistent_property("y_scan_y", [])
    y_scan_I = persistent_property("y_scan_I", [])
    y_scan_sigI = persistent_property("y_scan_sigI", [])

    cancelled = persistent_property("cancelled", False)
    x_scan_started = persistent_property("x_scan_started", 0.0)
    y_scan_started = persistent_property("y_scan_started", 0.0)

    def get_x_control(self):
        return MirrorH.command_value

    def set_x_control(self, value):
        MirrorH.command_value = value

    x_control = property(get_x_control, set_x_control)

    def x_next(self, x):
        """The next value that is an intergal motor step"""
        offset = MirrorH.offset
        dx = self.settings.x_resolution
        return round_next(x - offset, dx) + offset

    def y_next(self, y):
        """The next value that is an integral motor step"""
        offset = 0  ##MirrorV.offset
        dy = self.settings.y_resolution
        return round_next(y - offset, dy) + offset

    def get_y_control(self):
        return MirrorV.command_value

    def set_y_control(self, value):
        MirrorV.command_value = value

    y_control = property(get_y_control, set_y_control)

    def x_pre_scan_setup(self):
        self.settings.x_aperture_norm = self.settings.x_aperture
        self.settings.x_aperture = self.settings.x_aperture_scan
        while self.settings.x_aperture_moving and not self.cancelled:
            sleep(0.1)
        ##xray_pulse.scope.sampling_mode = "RealTime" # Lauecollect uses "Sequence"
        xray_pulse.enabled = True

    def x_post_scan_setup(self):
        self.settings.x_aperture = self.settings.x_aperture_norm
        xray_pulse.enabled = False

    def y_pre_scan_setup(self):
        self.settings.y_aperture_norm = self.settings.y_aperture
        self.settings.y_aperture = self.settings.y_aperture_scan
        while self.settings.y_aperture_moving and not self.cancelled:
            sleep(0.1)
        ##xray_pulse.scope.sampling_mode = "RealTime" # Lauecollect uses "Sequence"
        xray_pulse.enabled = True

    def y_post_scan_setup(self):
        self.settings.y_aperture = self.settings.y_aperture_norm
        xray_pulse.enabled = False

    def timing_system_pre_scan_setup(self):
        # Save current settings
        self.settings.xosct_on_norm = self.settings.timing_sequencer.xosct_on
        self.settings.ms_on_norm = self.settings.timing_sequencer.ms_on

        if not self.settings.timing_sequencer.xosct_on:
            self.settings.timing_sequencer.xosct_on = True
        if not self.settings.timing_sequencer.ms_on:
            self.settings.timing_sequencer.ms_on = True
        while not (self.settings.timing_sequencer.xosct_on and self.settings.
                   timing_sequencer.ms_on) and not self.cancelled:
            sleep(0.1)

    def timing_system_post_scan_setup(self):
        # Restore settings
        self.settings.timing_sequencer.xosct_on = self.settings.xosct_on_norm
        self.settings.timing_sequencer.ms_on = self.settings.ms_on_norm

    def get_x_scan_running(self):
        return self.x_scan_started > time() - self.settings.scan_timeout

    def set_x_scan_running(self, value):
        if value:
            if not self.x_scan_running: self.start_x_scan()
        else: self.cancelled = True

    x_scan_running = property(get_x_scan_running, set_x_scan_running)

    def get_y_scan_running(self):
        return self.y_scan_started > time() - self.settings.scan_timeout

    def set_y_scan_running(self, value):
        if value:
            if not self.y_scan_running: self.start_y_scan()
        else: self.cancelled = True

    y_scan_running = property(get_y_scan_running, set_y_scan_running)

    def start_x_scan(self):
        self.cancelled = False
        start_new_thread(self.perform_x_scan, ())

    def start_y_scan(self):
        self.cancelled = False
        start_new_thread(self.perform_y_scan, ())

    def perform_x_scan(self):
        self.x_scan_started = time()
        self.x_pre_scan_setup()
        self.timing_system_pre_scan_setup()
        self.x_scan_x = []
        self.x_scan_I = []
        self.x_scan_sigI = []
        x0 = self.x_control
        # Start scanning in positive direction.
        dx = self.settings.dx_scan
        x = x0
        self.x_control = x
        I, sigI = self.I_sigI
        self.x_scan_x += [x]
        self.x_scan_I += [I]
        self.x_scan_sigI += [sigI]
        x += dx
        self.x_control = x
        I, sigI = self.I_sigI
        self.x_scan_x += [x]
        self.x_scan_I += [I]
        self.x_scan_sigI += [sigI]
        # If the intensity goes down, reverse direction.
        if self.x_scan_I[1] < self.x_scan_I[0]:
            dx = -dx
            x = x0
            self.x_scan_x = self.x_scan_x[::-1]
            self.x_scan_I = self.x_scan_I[::-1]
            self.x_scan_sigI = self.x_scan_sigI[::-1]
        x += dx
        self.x_control = x
        I, sigI = self.I_sigI
        self.x_scan_x += [x]
        self.x_scan_I += [I]
        self.x_scan_sigI += [sigI]
        # Continue scanning until a maximum is reached.
        while self.x_scan_I[-1] > self.x_scan_I[-2] and not self.cancelled:
            x += dx
            self.x_control = x
            I, sigI = self.I_sigI
            self.x_scan_x += [x]
            self.x_scan_I += [I]
            self.x_scan_sigI += [sigI]
        # Return to the starting point.
        self.x_control = x0
        self.x_post_scan_setup()
        self.timing_system_post_scan_setup()
        self.x_scan_started = 0

    def perform_y_scan(self):
        self.y_scan_started = time()
        self.y_pre_scan_setup()
        self.timing_system_pre_scan_setup()
        self.y_scan_y = []
        self.y_scan_I = []
        self.y_scan_sigI = []
        y0 = self.y_control
        # Start scanning in positive direction.
        dy = self.settings.dy_scan
        y = y0
        self.y_control = y
        I, sigI = self.I_sigI
        self.y_scan_y += [y]
        self.y_scan_I += [I]
        self.y_scan_sigI += [sigI]
        y += dy
        self.y_control = y
        I, sigI = self.I_sigI
        self.y_scan_y += [y]
        self.y_scan_I += [I]
        self.y_scan_sigI += [sigI]
        # If the intensity goes down, reverse direction.
        if self.y_scan_I[1] < self.y_scan_I[0]:
            dy = -dy
            y = y0
            self.y_scan_y = self.y_scan_y[::-1]
            self.y_scan_I = self.y_scan_I[::-1]
            self.y_scan_sigI = self.y_scan_sigI[::-1]
        y += dy
        self.y_control = y
        I, sigI = self.I_sigI
        self.y_scan_y += [y]
        self.y_scan_I += [I]
        self.y_scan_sigI += [sigI]
        # Continue scanning until a mayimum is reached.
        while self.y_scan_I[-1] > self.y_scan_I[-2] and not self.cancelled:
            y += dy
            self.y_control = y
            I, sigI = self.I_sigI
            self.y_scan_y += [y]
            self.y_scan_I += [I]
            self.y_scan_sigI += [sigI]
        # Return to the starting point.
        self.y_control = y0
        self.y_post_scan_setup()
        self.timing_system_post_scan_setup()
        self.y_scan_started = 0

    @property
    def x_control_corrected(self):
        if len(self.x_scan_x) < 3 or len(self.x_scan_I) < 3: return nan
        x = parabola_vertex(self.x_scan_x[-3:], self.x_scan_I[-3:])[0]
        x = self.x_next(x)
        return x

    @property
    def y_control_corrected(self):
        if len(self.y_scan_y) < 3 or len(self.y_scan_I) < 3: return nan
        y = parabola_vertex(self.y_scan_y[-3:], self.y_scan_I[-3:])[0]
        ##y = self.y_next(y)
        return y

    @property
    def x_scan_x_fit(self):
        return parabolic_fit(self.x_scan_x, self.x_scan_I)[0]

    @property
    def x_scan_I_fit(self):
        return parabolic_fit(self.x_scan_x, self.x_scan_I)[1]

    @property
    def y_scan_y_fit(self):
        return parabolic_fit(self.y_scan_y, self.y_scan_I)[0]

    @property
    def y_scan_I_fit(self):
        return parabolic_fit(self.y_scan_y, self.y_scan_I)[1]

    def apply_x_correction(self):
        self.x_control = self.x_control_corrected
        self.update_log()

    def apply_y_correction(self):
        self.y_control = self.y_control_corrected
        self.update_log()

    def update_log(self):
        """Create a log file entry every time a correction is applied"""
        self.log.log(self.x_control, self.y_control)

    @property
    def I_sigI(self):
        """X-ray beam intensity"""
        xray_pulse.reset_average()
        sleep(4)
        I = xray_pulse.average
        sI = xray_pulse.stdev
        N = xray_pulse.count
        sigI = I / sqrt(N - 1) if N > 1 else nan
        return I, sigI
Beispiel #19
0
class Xray_Beam_Position_Check(object):
    name = "xray_beam_position_check"

    class Settings(object):
        name = "settings"

        # X-Ray beam steering controls.
        # Horizontal deflection mirror jacks
        def get_x1_motor(self):
            return MirrorH.m1.prefix

        def set_x1_motor(self, value):
            MirrorH.m1.prefix = value

        x1_motor = property(get_x1_motor, set_x1_motor)

        def get_x2_motor(self):
            return MirrorH.m2.prefix

        def set_x2_motor(self, value):
            MirrorH.m2.prefix = value

        x2_motor = property(get_x2_motor, set_x2_motor)

        def get_y_motor(self):
            return MirrorV.prefix

        def set_y_motor(self, value):
            MirrorV.prefix = value

        y_motor = property(get_y_motor, set_y_motor)

        # To narrow down aperture upstream of the detector for higher senitivity
        def get_x_aperture_motor(self):
            return shg.prefix

        def set_x_aperture_motor(self, value):
            shg.prefix = value

        x_aperture_motor = property(get_x_aperture_motor, set_x_aperture_motor)

        def get_y_aperture_motor(self):
            return svg.prefix

        def set_y_aperture_motor(self, value):
            svg.prefix = value

        y_aperture_motor = property(get_y_aperture_motor, set_y_aperture_motor)

        x_aperture_norm = persistent_property("x_aperture_norm", 0.150)
        y_aperture_norm = persistent_property("y_aperture_norm", 0.050)
        x_aperture_scan = persistent_property("x_aperture_scan", 0.050)
        y_aperture_scan = persistent_property("y_aperture_scan", 0.020)

        def get_x_aperture(self):
            return shg.command_value

        def set_x_aperture(self, value):
            shg.command_value = value

        x_aperture = property(get_x_aperture, set_x_aperture)

        def get_y_aperture(self):
            return svg.command_value

        def set_y_aperture(self, value):
            svg.command_value = value

        y_aperture = property(get_y_aperture, set_y_aperture)

        def get_timing_system_ip_address(self):
            return timing_system.ip_address

        def set_timing_system_ip_address(self, value):
            timing_system.ip_address = value

        timing_system_ip_address = property(get_timing_system_ip_address,
                                            set_timing_system_ip_address)

        acquire_image_timeout = 30  # seconds

        x_gain = persistent_property("x_gain", 0.143)  # mrad/mm
        y_gain = persistent_property("y_gain", 2.7)  # V/mm was: 1/3.3e-3
        x_nominal = persistent_property("x_nominal",
                                        175.927)  # mm from left, 2016-03-05
        y_nominal = persistent_property("y_nominal",
                                        174.121)  # mm from top,  2016-03-05
        history_length = persistent_property("history_length", 50)
        average_samples = persistent_property("average_samples", 1)
        x_enabled = persistent_property("x_enabled", False)
        y_enabled = persistent_property("y_enabled", False)
        ROI_width = persistent_property("ROI_width", 1.0)  # mm
        x_ROI_center = persistent_property("x_ROI_center",
                                           175.9)  # mm from left
        y_ROI_center = persistent_property("y_ROI_center",
                                           174.1)  # mm from top
        min_SNR = persistent_property("min_SNR", 5.0)  # signal-to-noise ratio

        image_filename = persistent_property(
            "image_filename",
            "//mx340hs/data/rayonix_scratch/xray_beam_position.rx")

    settings = Settings()

    log = LogFile(
        name + ".log",
        ["date time", "x", "y", "x_control", "y_control", "image_timestamp"])
    if log.filename == "":
        log.filename = "//mx340hs/data/anfinrud_1611/Logfiles/xray_beam_position_check.log"

    def update(self):
        t = self.image_timestamp
        if t != 0 and abs(t - self.last_image_timestamp) >= 0.1:
            x, y = self.beam_position
            xc, yc = self.x_control, self.y_control
            self.log.log(x, y, xc, yc, t)

    @property
    def x_average(self):
        return average(self.x_samples)

    @property
    def y_average(self):
        return average(self.y_samples)

    @property
    def x_history(self):
        return self.log.history("x", count=self.settings.history_length)

    @property
    def y_history(self):
        return self.log.history("y", count=self.settings.history_length)

    @property
    def t_history(self):
        return self.log.history("date time",
                                count=self.settings.history_length)

    @property
    def x_samples(self):
        return self.log.history("x", count=self.settings.average_samples)

    @property
    def y_samples(self):
        return self.log.history("y", count=self.settings.average_samples)

    @property
    def last_image_timestamp(self):
        t = self.log.history("image_timestamp", count=1)
        t = t[0] if len(t) > 0 else 0
        return t

    def get_x_control(self):
        return tofloat(caget(self.x_read_PV))

    def set_x_control(self, value):
        return caput(self.x_PV, value)

    x_control = property(get_x_control, set_x_control)

    def get_y_control(self):
        return tofloat(caget(self.y_read_PV))

    def set_y_control(self, value):
        return caput(self.y_PV, value)

    y_control = property(get_y_control, set_y_control)

    def get_x_control_average(self):
        return average(self.x_control_samples)

    def set_x_control_average(self, value):
        self.x_control = value

    x_control_average = property(get_x_control_average, set_x_control_average)

    def get_y_control_average(self):
        return average(self.y_control_samples)

    def set_y_control_average(self, value):
        self.y_control = value

    y_control_average = property(get_y_control_average, set_y_control_average)

    @property
    def x_control_samples(self):
        return self.log.history("x_control",
                                count=self.settings.average_samples)

    @property
    def y_control_samples(self):
        return self.log.history("y_control",
                                count=self.settings.average_samples)

    @property
    def x_control_history(self):
        return self.log.history("x_control",
                                count=self.settings.history_length)

    @property
    def y_control_history(self):
        return self.log.history("y_control",
                                count=self.settings.history_length)

    @property
    def x_control_corrected(self):
        """Value for the y control in order to bring the x position back to
        its nominal value"""
        x_control = self.x_control_average - \
            (self.x_average - self.x_nominal)*self.settings.x_gain
        return x_control

    @property
    def y_control_corrected(self):
        """Value for the y control in order to bring the y position back to
        its nominal value"""
        y_control = self.y_control_average - \
            (self.y_average - self.y_nominal)*self.settings.y_gain
        return y_control

    def apply_correction(self):
        self.apply_x_correction()
        self.apply_y_correction()

    def apply_x_correction(self):
        self.x_control = self.x_control_corrected

    def apply_y_correction(self):
        self.y_control = self.y_control_corrected

    cancelled = persistent_property("cancelled", False)
    acquire_image_started = persistent_property("acquire_image_started", 0.0)

    def get_x_control(self):
        return MirrorH.command_value

    def set_x_control(self, value):
        MirrorH.command_value = value

    x_control = property(get_x_control, set_x_control)

    def x_next(self, x):
        """The next value that is an intergal motor step"""
        offset = MirrorH.offset
        dx = self.settings.x_resolution
        return round_next(x - offset, dx) + offset

    def y_next(self, y):
        """The next value that is an intergal motor step"""
        offset = 0  ##MirrorV.offset
        dy = self.settings.y_resolution
        return round_next(y - offset, dy) + offset

    def get_y_control(self):
        return MirrorV.command_value

    def set_y_control(self, value):
        MirrorV.command_value = value

    y_control = property(get_y_control, set_y_control)

    def acquire_image_setup(self):
        self.settings.x_aperture = self.settings.x_aperture_scan
        self.settings.y_aperture = self.settings.y_aperture_scan

    def acquire_image_unsetup(self):
        self.settings.x_aperture = self.settings.x_aperture_norm
        self.settings.y_aperture = self.settings.y_aperture_norm

    def get_acquire_image_running(self):
        return self.acquire_image_started > time(
        ) - self.settings.acquire_image_timeout

    def set_acquire_image_running(self, value):
        if value:
            if not self.acquire_image_running: self.start_acquire_image()
        else: self.cancelled = True

    acquire_image_running = property(get_acquire_image_running,
                                     set_acquire_image_running)

    def start_acquire_image(self):
        self.cancelled = False
        start_new_thread(self.acquire_image, ())

    def acquire_image(self):
        self.acquire_image_started = time()
        self.acquire_image_setup()

        ccd.ignore_first_trigger = False
        ccd.acquire_images_triggered([normpath(self.settings.image_filename)])
        show_images(normpath(self.settings.image_filename))
        Ensemble_SAXS.acquire(delays=[0], laser_on=[False])
        tmax = time() + self.settings.acquire_image_timeout
        while ccd.state() != "idle" and not self.cancelled and time() < tmax:
            sleep(0.05)
        ccd.abort()

        self.acquire_image_unsetup()
        self.acquire_image_started = 0
        self.update()

    @property
    def x_beam(self):
        return self.beam_position[0]

    @property
    def y_beam(self):
        return self.beam_position[1]

    @property
    def beam_position(self):
        xprofile, yprofile = xy_projections(self.image, self.ROI_center,
                                            self.ROI_width)
        x, y = CFWHM(xprofile), CFWHM(yprofile)
        return x, y

    @property
    def x_error(self):
        return self.x_beam - self.x_nominal

    @property
    def y_error(self):
        return self.y_beam - self.y_nominal

    @property
    def image_OK(self):
        return not self.image_overloaded and self.SNR > self.settings.min_SNR

    @property
    def image_overloaded(self):
        return overloaded_pixels(self.image, self.ROI_center, self.ROI_width)

    @property
    def SNR(self):
        xprofile, yprofile = xy_projections(self.image, self.ROI_center,
                                            self.ROI_width)
        return (SNR(xprofile) + SNR(yprofile)) / 2

    @property
    def image(self):
        from numimage import numimage
        filename = normpath(self.settings.image_filename)
        if exists(filename): image = numimage(filename)
        else: image = self.default_image
        # Needed for "BeamProfile" to detect image updates.
        # If a memory-mapped image would be chached by "BeamProfile", the
        # comparison with the new image would show no difference, since the
        # cached image dynamically updates.
        image = image.copy()
        return image

    @property
    def x_ROI_center(self):
        return self.settings.x_ROI_center

    @property
    def y_ROI_center(self):
        return self.settings.y_ROI_center

    @property
    def ROI_center(self):
        return self.x_ROI_center, self.y_ROI_center

    @property
    def ROI_width(self):
        return self.settings.ROI_width

    @property
    def x_nominal(self):
        return self.settings.x_nominal

    @property
    def y_nominal(self):
        return self.settings.y_nominal

    @property
    def default_image(self):
        from numimage import numimage
        from numpy import uint16
        image = numimage((3840, 3840), pixelsize=0.0886, dtype=uint16) + 10
        return image

    @property
    def image_timestamp(self):
        """Full pathname of the last recorded image"""
        from os.path import getmtime, dirname
        from os import listdir
        filename = normpath(self.settings.image_filename)
        if exists(filename):
            listdir(dirname(filename))  # for NFS attibute caching
        t = getmtime(filename) if exists(filename) else 0
        return t