def run(self): """Thread loop""" log.info('Waiting %ds...' % self.start_interval) time.sleep(self.start_interval) log.info('Starting playlist') if settings.inputlistener and KeyListener.device_available: background = KeyListener(self) background.start() while (app.running): log.debug("Current playlist: %s" % self.playlist) self.index = 0 self.size = len(self.playlist['screens']) while (self.index < self.size): # reset shared data try: server_item = beacon.queue.get_nowait() log.debug("Current server item: %s" % server_item) stay = self.handle_item(server_item) beacon.queue.task_done() if not stay: log.debug("Exiting loop") break except Empty: pass item = self.playlist['screens'][self.index] log.debug("Current item: %s" % self.index) if app.running: # state may have changed self.handle_item(item) self.index += 1 log.info("Exiting player thread.")
def launch_keylisten_thread(self): """ Launch a KeyListener thread to get key presses in the command line """ self.keylisten_thread = KeyListener(self.on_key_press) self.keylisten_thread.setDaemon(True) self.logger.info("starting keylistener") self.keylisten_thread.start()
from keylistener import KeyListener from sqliterecorder import SQLiteRecorder listener = KeyListener() recorder = SQLiteRecorder() listener.set_recorder(lambda x : recorder.record(x)) listener.setup() i = "" while i != "stop": i = raw_input()
for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.MOUSEMOTION: MouseListener.update_pos(event) elif event.type == pygame.MOUSEBUTTONUP: MouseListener.update_button_up(event) elif event.type == pygame.MOUSEBUTTONDOWN: MouseListener.update_button_down(event) elif event.type == pygame.KEYDOWN: KeyListener.update_key_down(event) elif event.type == pygame.KEYUP: KeyListener.update_key_up(event) current_state.update() MouseListener.update() KeyListener.update() game_display.fill((255, 255, 255)) current_state.draw() pygame.display.update()
def main(): bridge = "192.168.1.3" username = "******" # Mapping a numbering to whatever numerical # id the bridge has given to bulbs/lights. bulb2hue = {1: 10, 2: 12, 3: 11, 4: 9, 5: 7, 6: 8} # Mapping the numerical keys to keycodes keys = {"7": 74, "8": 78, "9": 96, "4": 73, "5": 77, "6": 81, "1": 72, "2": 76, "3": 80, "0": 71, "*": 75, "#": 79} # Keyboard device dev = "/dev/input/by-id/usb-GASIA_USB_KB_V11-event-kbd" b = Bridge(bridge, username) # Construct Bridge object l = KeyListener(dev) # Contruct keyboard listener # Register callbacks l.register_keyPress(keys["0"], lambda event: b.toggle()) l.register_keyPress(keys["1"], lambda event: b.toggle(bulb2hue[1])) l.register_keyPress(keys["2"], lambda event: b.toggle(bulb2hue[2])) l.register_keyPress(keys["3"], lambda event: b.toggle(bulb2hue[3])) l.register_keyPress(keys["4"], lambda event: b.toggle(bulb2hue[4])) l.register_keyPress(keys["5"], lambda event: b.toggle(bulb2hue[5])) l.register_keyPress(keys["6"], lambda event: b.toggle(bulb2hue[6])) # l.register_keyPress(keys["6"], lambda event: b.set_state(states["Energize"])) l.register_keyPress(keys["7"], lambda event: b.set_state(states["Concentrate"])) l.register_keyPress(keys["8"], lambda event: b.set_state(states["Reading"])) l.register_keyPress(keys["9"], lambda event: b.set_state(states["Relax"])) l.register_keyPress(keys["*"], lambda event: b.set_bri({k: v - 51 for (k, v) in b.get_bri().items()})) l.register_keyPress(keys["#"], lambda event: b.set_bri({k: v + 51 for (k, v) in b.get_bri().items()})) l.listen() # Listen for events
#!/usr/bin/env python from pymouse import PyMouse from time import sleep from functools import wraps from keylistener import KeyListener k = KeyListener() WIDTH, HEIGHT = k.get_geometry() CLICK_DELAY = 0.3 XY = {} XY['end_turn'] = int(WIDTH * (HEIGHT / WIDTH < 0.75 and 0.92 or 0.80)), int(HEIGHT * 0.46) XY['hero_power'] = int(WIDTH / 2 + WIDTH / 9), int(HEIGHT * 3 / 4) XY['hero'] = WIDTH / 2, int(HEIGHT * 3 / 4) XY['enemy'] = WIDTH / 2, int(HEIGHT / 6) XY['play_btn'] = int(WIDTH * 5 / 6), int(HEIGHT * 5 / 6) m = PyMouse() def keep_mouse_position(mouse_moving_func): def _preserve(*args, **kwargs): _x, _y = m.position() mouse_moving_func(*args, **kwargs) m.move(_x, _y) return wraps(mouse_moving_func)(_preserve) def click(what, btn=1, delay=CLICK_DELAY): m.click(XY[what][0], XY[what][1], btn) sleep(delay)
def main(): bridge = "192.168.1.3" username = "******" # Mapping a numbering to whatever numerical # id the bridge has given to bulbs/lights. bulb2hue = {1: 10, 2: 12, 3: 11, 4: 9, 5: 7, 6: 8} # Mapping the numerical keys to keycodes keys = { "7": 74, "8": 78, "9": 96, "4": 73, "5": 77, "6": 81, "1": 72, "2": 76, "3": 80, "0": 71, "*": 75, "#": 79, } # Keyboard device dev = "/dev/input/by-id/usb-GASIA_USB_KB_V11-event-kbd" b = Bridge(bridge, username) # Construct Bridge object l = KeyListener(dev) # Contruct keyboard listener # Register callbacks l.register_keyPress(keys["0"], lambda event: b.toggle()) l.register_keyPress(keys["1"], lambda event: b.toggle(bulb2hue[1])) l.register_keyPress(keys["2"], lambda event: b.toggle(bulb2hue[2])) l.register_keyPress(keys["3"], lambda event: b.toggle(bulb2hue[3])) l.register_keyPress(keys["4"], lambda event: b.toggle(bulb2hue[4])) l.register_keyPress(keys["5"], lambda event: b.toggle(bulb2hue[5])) l.register_keyPress(keys["6"], lambda event: b.toggle(bulb2hue[6])) #l.register_keyPress(keys["6"], lambda event: b.set_state(states["Energize"])) l.register_keyPress(keys["7"], lambda event: b.set_state(states["Concentrate"])) l.register_keyPress(keys["8"], lambda event: b.set_state(states["Reading"])) l.register_keyPress(keys["9"], lambda event: b.set_state(states["Relax"])) l.register_keyPress( keys["*"], lambda event: b.set_bri({k: v - 51 for (k, v) in b.get_bri().items()})) l.register_keyPress( keys["#"], lambda event: b.set_bri({k: v + 51 for (k, v) in b.get_bri().items()})) l.listen() # Listen for events
def run(self): listener = KeyListener() listener.start()
class PXI: """ PXI Class. TODO: write docstring Attributes: """ help_str = ("At any time, type... \n" + " - 'h' to see this message again \n" + " - 'r' to reset the connection to CsPy \n" + " - 'd' to toggle the server DEBUG level logging \n" + " - 'x' to print the most recently received xml to a file \n" + " - 'q' to stop the connection and close this server.") def __init__(self, address: Tuple[str, int]): self.root_logger = logging.getLogger() # root_logger self._root_logging_lvl_default = self.root_logger.level self.sh = self.root_logger.handlers[0] # stream_handler self._sh_lvl_default = self.sh.level self.logger = logging.getLogger(self.__class__.__name__) self.logger.setLevel(logging.DEBUG) fh = logging.FileHandler('spam.log') fh.setLevel(logging.DEBUG) self.logger.addHandler(fh) self._stop_connections = False self._reset_connection = False self._exit_measurement = False self.cycle_continuously = False self.return_data = b"" self.return_data_queue = b"" self.measurement_timeout = 0 self.keylisten_thread = None self.command_queue = Queue(0) # 0 indicates no maximum queue length enforced. self.element_tags = [] # for debugging self.devices = [] # instantiate the device objects self.hsdio = HSDIO(self) self.tcp = TCP(self, address) self.analog_input = AnalogInput(self) self.analog_output = AnalogOutput(self) self.ttl = TTLInput(self) # self.daqmx_do = DAQmxDO(self) self.hamamatsu = Hamamatsu(self) self.counters = Counters(self) @property def stop_connections(self) -> bool: return self._stop_connections @stop_connections.setter def stop_connections(self, value): self._stop_connections = value @property def reset_connection(self) -> bool: return self._reset_connection @reset_connection.setter def reset_connection(self, value): self._reset_connection = value @property def exit_measurement(self) -> bool: return self._exit_measurement @exit_measurement.setter def exit_measurement(self, value): self._exit_measurement = value @property def active_devices(self): """ Number of devices that were successfully initialized """ return sum(dev.is_initialized for dev in self.devices) @property def root_logging_lvl_default(self): """ The default root logging level for this server """ return self._root_logging_lvl_default @property def sh_lvl_default(self): """ The default stream handler level for this server """ return self._sh_lvl_default def queue_command(self, command): self.command_queue.put(command) def launch_network_thread(self): self.tcp.launch_network_thread() def launch_experiment_thread(self): """ Launch a thread for the main experiment loop Thread target method = self.command_loop """ self.experiment_thread = threading.Thread( target=self.command_loop, name='Experiment Thread' ) self.experiment_thread.setDaemon(False) self.experiment_thread.start() def command_loop(self): """ Update devices with xml from CsPy, and get and return data from devices Pop a command from self.command_queue on each iteration, parse the xml in that command, and update the instruments accordingly. When the queue is empty, try to receive measurements from the data if cycling continuously. This function handles the switching between updating devices and getting data from them, while the bulk of the work is done in the hierarchy of methods in self.parse_xml and self.measurement. """ while not (self.stop_connections or self.exit_measurement): try: # dequeue xml xml_str = self.command_queue.get(block=False, timeout=0) self.parse_xml(xml_str) except Empty: self.exit_measurement = False self.return_data = b"" # clear the return data if self.cycle_continuously and self.active_devices > 0: self.logger.debug("Entering cycle continously...") # This method returns the data self.return_data_queue = self.measurement() sleep(0.001) # keep while loop from hogging resources self.shutdown() self.logger.info("Exiting Experiment Thread.") def launch_keylisten_thread(self): """ Launch a KeyListener thread to get key presses in the command line """ self.keylisten_thread = KeyListener(self.on_key_press) self.keylisten_thread.setDaemon(True) self.logger.info("starting keylistener") self.keylisten_thread.start() def parse_xml(self, xml_str: str): """ Initialize the device instances and other settings from queued xml Loop over highest tier of xml tags with the root tag='LabView' in the message received from CsPy, and call the appropriate device class accordingly. the xml is popped from a queue, which updates in the network_loop whenever a valid message from CsPy is received. Args: 'xml_str': (str) xml received from CsPy in the receive_message method """ self.exit_measurement = False self.element_tags = [] # clear the list of received tags # get the xml root root = ET.fromstring(xml_str) if root.tag != "LabView": self.logger.warning("Not a valid msg for the pxi") else: # loop non-recursively over children in root to setup device # hardware and other server settings for child in root: self.element_tags.append(child) try: if child.tag == "measure": # if no data available, take one measurement. Otherwise, # use the most recent data. if self.return_data_queue == b"": self.measurement() else: self.return_data = self.return_data_queue pass elif child.tag == "pause": # TODO: set state of server to 'pause'; # i don't know if this a feature that currently gets used, # so might be able to omit this. pass elif child.tag == "run": # TODO: set state of server to 'run'; # i don't know if this a feature that currently gets used, # so might be able to omit this. pass elif child.tag == "HSDIO": # setup the HSDIO self.hsdio.load_xml(child) self.logger.info("HSDIO XML loaded") self.hsdio.init() self.logger.info("HSDIO hardware initialized") self.hsdio.update() self.logger.info("HSDIO hardware updated") elif child.tag == "TTL": self.ttl.load_xml(child) self.logger.info("TTLInput XML loaded") self.ttl.init() self.logger.info("TTLInput hardware initialized") elif child.tag == "DAQmxDO": # self.daqmx_do.load_xml(child) # self.daqmx_do.init() pass elif child.tag == "timeout": try: # get timeout in [ms] self.measurement_timeout = 1000 * float(child.text) except ValueError as e: msg = f"{e} \n {child.text} is not valid" + \ f"text for node {child.tag}" raise XMLError(self, child, message=msg) elif child.tag == "cycleContinuously": cycle = False if child.text.lower() == "true": cycle = True self.cycle_continuously = cycle elif child.tag == "camera": # set up the Hamamatsu camera self.hamamatsu.load_xml(child) # Raises ValueError self.hamamatsu.init() # Raises IMAQErrors elif child.tag == "AnalogOutput": # set up the analog_output self.analog_output.load_xml(child) self.logger.info("AnalogOutput XML loaded") self.analog_output.init() self.logger.info("AnalogOutput initialized") self.analog_output.update() self.logger.info("AnalogOutput hardware updated") elif child.tag == "AnalogInput": # set up the analog_input self.analog_input.load_xml(child) self.analog_input.init() elif child.tag == "Counters": # # TODO: implement counters class # # set up the counters self.counters.load_xml(child) self.counters.init() pass # # might implement, or might move RF generator functionality to # # CsPy based on code used by Hybrid. elif child.tag == "RF_generators": pass else: self.logger.warning(f"Node {child.tag} received is not a valid" + f"child tag under root <{root.tag}>") # I do not catch AssertionErrors. The one at the top of load_xml in every # device class can only occur if the device is passed the wrong xml node, # which can never occur in pxi.parse_xml, as we check the tag before # instantiating a device. those assertions are there in case someone down the # road does something more careless. except (XMLError, HardwareError) as e: self.handle_errors(e) # send a message back to CsPy self.tcp.send_message(self.return_data) # clear the return data self.return_data = b"" self.return_data_queue = b"" def data_to_xml(self) -> str: """ Get xml-formatted data string from device measurements Return the data as an xml string by calling the device class is_out methods. Returns: 'return_data': concatenated string of xml-formatted data """ return_data = b"" # the devices which have a method named 'data_out' which returns a str devices = [ self.hamamatsu, self.counters, #self.ttl, # self.counters, #TODO: implement self.ttl, self.analog_input # self.demo # not implemented, and debatable whether it needs to be ] for dev in devices: if dev.is_initialized: try: return_data += dev.data_out() except HardwareError as e: self.handle_errors(e) self.return_data = return_data return return_data def measurement(self) -> str: """ Return a queue of the acquired responses queried from device hardware Returns: 'return_data': string of concatenated responses received from the device classes """ if not (self.stop_connections or self.exit_measurement): # self.logger.info(f"measurement time lapse= {time()-self.trelative}") self.trelative = time() self.reset_data() self.system_checks() self.start_tasks() _is_done = False _is_error = False ## timed loop to frequently check if tasks are done tau = 10 # loop period in [ms] scl = 1e-6 # scale factor to convert ns to ms devtime = time() finished_devs = [] while not (_is_done or _is_error or self.stop_connections or self.exit_measurement): try: _is_done = self.is_done() except HardwareError as e: self.handle_errors(e) # sleep to reduce resource usage sleep(0.001) try: self.get_data() self.system_checks() self.stop_tasks() return_data = self.data_to_xml() return return_data except Exception as e: # TODO: make less general self.logger.warning(f"Error encountered {e}\nNo data returned.") self.handle_errors(e) return "" def reset_data(self): """ Resets data on devices which need to be reset. For now, only applies to TTL """ try: self.ttl.reset_data() except HardwareError as e: self.handle_errors(e) def system_checks(self): """ Check devices. For now, only applies to TTL """ try: self.ttl.check() except HardwareError as e: self.handle_errors(e) # wrap batch_method_call calls in convenience functions def start_tasks(self, handle_error=True): """ Start measurement and output tasks for relevant devices """ # devices which have a method 'start' devices = [ self.hsdio, # self.daqmx_do, # self.hamamatsu, self.analog_input, self.analog_output, #self.ttl self.counters ] self.batch_method_call(devices, 'start', handle_error) # self.reset_timeout() # TODO : Implement or discard def stop_tasks(self, handle_error=True): """ Stop measurement and output tasks for relevant devices """ # devices which have a method 'stop' devices = [ self.hsdio, # self.daqmx_do, self.analog_input, self.analog_output, #self.ttl self.counters # TODO: implement Counters.stop ] self.batch_method_call(devices, 'stop', handle_error) def close_tasks(self, handle_error=True): """ Close references to tasks for relevant devices """ # devices which have a method 'stop' devices = [ self.hsdio, # self.daqmx_do, self.hamamatsu, self.analog_input, self.analog_output, self.ttl, self.counters # TODO: implement Counters.stop ] self.batch_method_call(devices, 'close', handle_error) def get_data(self, handle_error=True): """ Get data from the devices """ if not (self.stop_connections or self.exit_measurement): # devices which have a method 'get_data' devices = [ self.hamamatsu, self.analog_input, self.counters # TODO: implement Counters.get_data ] self.batch_method_call(devices, 'get_data', handle_error) def is_done(self) -> bool: """ Check if devices running processes are done yet Loops over the device classes and calls the instance's is_done method for each device capable of running a process and breaks when a process is found to not be done. Returns: done 'done': will return True iff all the device processes are done. """ done = True if not (self.stop_connections or self.exit_measurement): # devices which have a method named 'is_done' that returns a bool devices = [ self.hsdio, self.analog_output, self.analog_input, # self.daqmx_do #self.counters ] try: for dev in devices: if dev.is_initialized: if not dev.is_done(): done = False break except HardwareError as e: self.handle_errors(e) return done return done def reset_timeout(self): """ Seems to change a global variable 'Timeout Elapses at' to the current time + timeout Will that work here? Returns: """ # TODO: Implement pass def on_key_press(self, key: str): """ Determines what happens for key presses in the command prompt. This method to be passed into the KeyListener instance to be called when keys are pressed. Args: 'key': the returned key from msvcrt.getwch(), e.g. 'h' """ if key == 'h': # show the help str self.logger.info(self.help_str) if key == 'r': # reset the connection self.logger.info("Connection reset by user.") self.reset_connection = True if key == 'x': # print most recently received xml to file try: fname = self.tcp.xml_to_file() self.logger.info("wrote xml to file "+fname) except Exception as e: self.logger.error(f"oops. failed to write to file. + \n {e}") if key == 'd': # toggle debug/info level root logging if self.root_logger.level != logging.DEBUG: self.root_logger.setLevel(logging.DEBUG) else: self.root_logger.setLevel(self.root_logging_lvl_default) self.logger.info("set the root level logging to default") if self.sh.level != logging.DEBUG: self.sh.setLevel(logging.DEBUG) else: self.sh.setLevel(self.sh_lvl_default) self.logger.info("set the stream handler level logging to default") self.logger.debug("Logging level is now DEBUG") elif key == 'q': self.logger.info("Connection stopped by user. Closing server.") self.exit_measurement = True self.stop_connections = True else: self.logger.info("Not a valid keypress. Type \'h\' for help.") def handle_errors(self, error: Exception, traceback_str: str = ""): """ Handle errors caught in the PXI instance Error handling philosophy: I. The class where the error originated should log a useful message detailing the source of the error II. If error should halt instrument activity, error should be raised to pxi class. Use logger.error(msg,exc_info=True) to log error with traceback. III. If error should not halt instrument activity, use logger.warning(msg,exc_info=t/f) (t/f = with/without traceback) IV. When error is raised to pxi class, this function should be called. A message will be logged to the terminal output as well as sent to CsPy, warning the user that an error has occurred and that the PXI settings should be updated. V. The current measurement cycle should be aborted, and restarted if self.cycle_continuously == True. Device where error occurred should be excluded from the measurement cycle until it has been reinitialized. I.e., devices which read/write signals (e.g. TTLInput, HSDIO, etc) should continue to cycle if they can VI. The errors raised in the device classes can should be classified coarsely, as many errors should result in the same handling behavior in this function (e.g. all error types pertaining to an XML initialization failure should result in a message urging the user to check the XML in CsPy and reinitialize the device). Specific error types handled here are HardwareError and XMLError, defined in pxierrors.py Args: error : The error that was raised. Maybe it's useful to keep it around traceback_str : string useful for traceback """ # NOTE: # This code is not a janitor, your mother/father, etc. If your device soiled itself, it is your # responsibility to clean up the problem where it occurred. The code that follows is only # intended to change the state the of the server and/or log a report of what happened, # in response to whatever mess was made. if self.stop_connections: return self.logger.warning("PXIError encountered. Closing the problematic tasks.") if isinstance(error, XMLError): self.logger.error(traceback_str + "\n" + error.message + "\n" + "Fix the pertinent XML in CsPy, then try again.") self.cycle_message(error.device) self.reset_exp_thread() elif isinstance(error, HardwareError): self.logger.error(traceback_str + "\n" + error.message) self.cycle_message(error.device) self.stop_tasks(handle_error=False) # stop all current measurement tasks error.device.close() # close the reference to the problematic device self.reset_exp_thread() # this stalls the program currently # If not a type of error we anticipated, raise it. else: raise error def cycle_message(self, dev: XMLLoader): """ Log a message about the status of the server cycling. Args: dev: the device instance where the problem occurred """ if self.cycle_continuously: self.logger.warning(f"The server will now cycle, but without {dev}") else: self.logger.info(f"The server is not taking data. Cycle continuously to resume, but without {dev}") def reset_exp_thread(self): """ Restart experiment thread after current measurement ends """ # self.logger.info("Waiting for the experiment thread to end...") # self.exit_measurement = True # while self.experiment_thread.is_alive(): # pass # self.experiment_thread.join() self.logger.info("Restarting current experiment thread...") self.exit_measurement = False # overwrite the existing thread ref. might be a better way to do this. self.launch_experiment_thread() self.logger.info("Experiment thread relaunched") def batch_method_call( self, device_list: List[Instrument], method: str, handle_error: bool = True ): """ Call a method common to several device classes Given a list of device instances, assumed to have method 'method' as well as bool parameter 'is_initialized', 'method' will be called for each instance. For now, it is assumed that 'method' takes no arguments, and does does not return anything. Args: device_list: list of device instances method: name of the method to be called. make sure every device in device list has a method with this name. handle_error: Should self.handle_errors() be called to deal with errors during this operation? """ # only iterate over initialized devices for dev in filter(lambda x: x.is_initialized, device_list): fun = getattr(dev, method, None) if fun is None or not callable(fun): self.logger.warning(f"{dev} does not have a method '{method}'") continue try: fun() # call the method # self.logger.info("starting the {dev.__class__.__name__}") except HardwareError as he: self.logger.info( f"Error {he} encountered while performing {dev}.{method}()" f"handle_error = {handle_error}") if handle_error: self.handle_errors(he) def shutdown(self): """ Nicely shut down this server """ if self.tcp.seeking_connection: self.tcp.abort() self.logger.info(f"Attempting to stop devices with stop method") self.stop_tasks() self.logger.info(f"Attempting to close devices with close method") self.close_tasks() self.logger.info(f"Disabling all devices") for device in self.devices: device.enable = False