def get_devices(config, force_reload=False): """ Get initialized and configured device instances. :param config: Global configuration :type config: :py:class:`spreads.config.Configuration` :param force_reload: Don't load devices from cache :type force_reload: bool :return: Device instances :rtype: list of :py:class:`DeviceDriver` objects """ # Reference to global device cache global devices if not devices or force_reload: if 'driver' not in config.keys(): raise DeviceException( "No driver has been configured\n" "Please run `spread configure` to select a driver.") driver = get_driver(config["driver"].get()) logger.debug("Finding devices for driver \"{0}\"" .format(driver.__name__)) devices = list(driver.yield_devices(config['device'])) if not devices: raise DeviceException( "Could not find any compatible devices!\n" "Make sure your devices are turned on and properly connected " "to the machine.") return devices
def _set_device_target_page(config, target_page): """ Display dialog for setting the target page on a device. :param config: Currently active global configuration :type config: :py:class:`spreads.config.Configuration` :param target_page: Target page to set on the device :type target_page: One of 'odd' or 'even' """ print("Please connect and turn on the device labeled \'{0}\'".format( target_page)) print("Press any key when ready.") getch() devs = plugin.get_devices(config, force_reload=True) if len(devs) > 1: raise DeviceException("Please ensure that only one device is" " turned on!") if not devs: raise DeviceException("No device found!") devs[0].set_target_page(target_page) print( colorize("Configured \'{0}\' device.".format(target_page), colorama.Fore.GREEN)) print("Please turn off the device.") print("Press any key when ready.") getch()
def _get_messages(self, script_id): msg = None retvals = [] # Get all messages returned by the script while not retvals or msg[2] != 0: time.sleep(0.01) try: msg = self._device.chdkReadScriptMessage() except PTPError: self.logger.warn("Couldn't read message, retrying...") continue if msg[1] == 1: raise DeviceException("Lua error: {0}".format(msg[0])) if msg[2] == 0: continue if msg[2] != script_id: self.logger.warn("Script IDs did not match. Expected \"{0}\"," " got \"{1}\", ignoring".format( script_id, msg[2])) self.logger.debug("Message (type {0}) was: {1}".format( msg[1], msg[0])) continue self.logger.debug("Camera returned: {0}".format(msg[0])) retvals.append(msg[0]) # NOTE: Just to be safe... time.sleep(0.25) # Return a tuple containing all of the messages return tuple(retvals)
def get_devices(config, force_reload=False): """ Initialize configured devices. """ global devices if not devices or force_reload: if 'driver' not in config.keys(): raise DeviceException( "No driver has been configured\n" "Please run `spread configure` to select a driver.") driver = get_driver(config["driver"].get()) logger.debug("Finding devices for driver \"{0}\"".format( driver.__name__)) devices = list(driver.yield_devices(config['device'])) if not devices: raise DeviceException("Could not find any compatible devices!") return devices
def prepare_capture(self): self._logger.info("Preparing capture.") self.step = 'capture' if any(dev.target_page is None for dev in self.devices): raise DeviceException( "Target page for at least one of the devicescould not be" "determined, please run 'spread configure' to configure your" "your devices.") with ThreadPoolExecutor(len(self.devices)) as executor: futures = [] self._logger.debug("Preparing capture in devices") for dev in self.devices: futures.append(executor.submit(dev.prepare_capture, self.path)) check_futures_exceptions(futures) flip_target = ('flip_target_pages' in self.config['device'].keys() and self.config['device']['flip_target_pages'].get()) if flip_target: (self.devices[0].target_page, self.devices[1].target_page) = (self.devices[1].target_page, self.devices[0].target_page) self._run_hook('prepare_capture', self.devices, self.path) self._run_hook('start_trigger_loop', self.capture) self.prepared = True self.active = True
def devices(self): if 'driver' not in self.config.keys(): raise DeviceException( "No driver has been configured\n" "Please run `spread configure` to select a driver.") if self._devices is None: self._devices = plugin.get_devices(self.config, force_reload=True) if any(not dev.connected() for dev in self._devices): self._logger.warning( "At least one of the devices has been disconnected." "Please make sure it has been re-enabled before taking another" "action.") self._devices = None if not self._devices: raise DeviceException("Could not find any compatible devices!") return self._devices
def _find_devices(self): for candidate in hidapi.enumerate(): try: dev = hidapi.Device(candidate, blocking=False) except IOError: raise DeviceException("Could not open HID device, please check" " your permissions on /dev/bus/usb.") yield dev
def configure(args=None): for orientation in ('left', 'right'): print("Please connect and turn on the device labeled \'{0}\'".format( orientation)) print(colorama.Fore.BLUE + "Press any key when ready.") getch() devs = get_devices() if len(devs) > 1: raise DeviceException("Please ensure that only one device is" " turned on!") if not devs: raise DeviceException("No device found!") devs[0].set_orientation(orientation) print(colorama.Fore.GREEN + "Configured \'{0}\' device.".format(orientation)) print("Please turn off the device.") print(colorama.Fore.BLUE + "Press any key when ready.") getch()
def _set_device_target_page(config, target_page): print("Please connect and turn on the device labeled \'{0}\'".format( target_page)) print("Press any key when ready.") getch() devs = plugin.get_devices(config, force_reload=True) if len(devs) > 1: raise DeviceException("Please ensure that only one device is" " turned on!") if not devs: raise DeviceException("No device found!") devs[0].set_target_page(target_page) print( colorize("Configured \'{0}\' device.".format(target_page), colorama.Fore.GREEN)) print("Please turn off the device.") print("Press any key when ready.") getch()
def execute_lua(self, script, wait=True, get_result=False, timeout=256): """ Executes a Lua script on the camera. :param script: The Lua script :type script: str :param wait: Wait for the script to complete :type wait: bool :param get_result: Return the script's return value :type get_result: bool :return: The script's return value (tuple) if get_result was True, or None """ # Wrap the script in return if the script doesn't return by itself if get_result and not "return" in script: script = "return({0})".format(script) retries = 0 while True: if retries >= 3: raise DeviceException("An error occured while executing" " \"{0}\"".format(script)) self.logger.debug("Executing script: \"{0}\"".format(script)) try: script_id = self._device.chdkExecLua(script) except Exception: self.logger.warn("Script raised an error.") self.logger.debug("Details:", exc_info=True) self.logger.warn("Retrying in 5s...") retries += 1 time.sleep(5) continue script_status = None if not wait: return # Wait for the script to complete loops = 0 while loops < timeout and script_status not in (1, 2): loops += 1 try: script_status = self._device.chdkScriptStatus(script_id) except PTPError: pass time.sleep(0.01) if not script_status: self.logger.warn("Script timed out, retrying...") retries += 1 else: break if get_result: return self._get_messages(script_id) else: self._flush_messages() return
def _set_zoom(self, level): """ Set zoom level. :param level: The zoom level to be used :type level: int """ available_levels = self._device.execute_lua("get_zoom_steps()", get_result=True)[0] if level > available_levels: raise DeviceException("Level outside of supported range!") self._device.execute_lua('set_zoom({0})'.format(level))
def _find_devices(self): """ Find all attached USB HID devices. :returns: All devices found :rtype: Generator that yields :py:class:`hidapi.Device` """ for candidate in hidapi.enumerate(): try: dev = hidapi.Device(candidate, blocking=False) except IOError: raise DeviceException("Could not open HID device, please check" " your permissions on /dev/bus/usb.") yield dev
def capture(args=None, devices=None): if not devices: devices = get_devices() if len(devices) != 2: raise DeviceException("Please connect and turn on two" " pre-configured devices! ({0} were" " found)".format(len(devices))) print(colorama.Fore.GREEN + "Found {0} devices!".format(len(devices))) if any(not x.orientation for x in devices): raise DeviceException("At least one of the devices has not been" " properly configured, please re-run the" " program with the \'configure\' option!") # Set up for capturing print("Setting up devices for capturing.") workflow.prepare_capture(devices) # Start capture loop print(colorama.Fore.BLUE + "Press 'b' to capture.") shot_count = 0 start_time = time.time() pages_per_hour = 0 capture_keys = config['capture']['capture_keys'].as_str_seq() while True: if not getch().lower() in capture_keys: break workflow.capture(devices) shot_count += len(devices) pages_per_hour = (3600/(time.time() - start_time))*shot_count status = ("\rShot {0} pages [{1:.0f}/h]" .format(colorama.Fore.GREEN + unicode(shot_count), pages_per_hour)) sys.stdout.write(status) sys.stdout.flush() workflow.finish_capture(devices) sys.stdout.write("\rShot {0} pages in {1:.1f} minutes, average speed was" " {2:.0f} pages per hour" .format(colorama.Fore.GREEN + str(shot_count), (time.time() - start_time)/60, pages_per_hour)) sys.stdout.flush()
def get_devices(): """ Detect all attached devices and select a fitting driver. :returns: list(DevicePlugin) -- All supported devices that were detected """ def match(extension, device): try: devname = usb.util.get_string(device, 256, 2) except: devname = "{0}:{1}".format(device.bus, device.address) logger.debug("Trying to match device \"{0}\" with plugin {1}".format( devname, extension.plugin.__name__)) try: match = extension.plugin.match(device) # Ignore devices that don't implement `match` except TypeError: logger.debug("Plugin did not implement match method!") return if match: logger.debug("Plugin matched device!") return extension.plugin logger.debug("Detecting support for attached devices") devices = [] candidates = usb.core.find(find_all=True) devicemanager = get_devicemanager() for device in candidates: matches = filter(None, devicemanager.map(match, device)) # FIXME: Make this more robust: What if, for instance, two plugins # are found for a device, one of which inherits from the other? if matches: devices.append(matches[0](spreads.config, device)) if not devices: raise DeviceException("Could not find any compatible devices!") return devices
def capture(config): """ Dialog to run through the capture process. :param config: Currently active global configuration :type config: :py:class:`spreads.config.Configuration` """ path = config['path'].get() workflow = spreads.workflow.Workflow(config=config, path=path) spreads.workflow.on_created.send(workflow) capture_keys = workflow.config['core']['capture_keys'].as_str_seq() # Some closures def _refresh_stats(): """ Callback that prints up-to-date capture statistics to stdout """ if _refresh_stats.start_time is not None: pages_per_hour = ((3600 / (time.time() - _refresh_stats.start_time)) * len(workflow.pages)) else: pages_per_hour = 0.0 _refresh_stats.start_time = time.time() status = ("\rShot {0: >3} pages [{1: >4.0f}/h] ".format( len(workflow.pages), pages_per_hour)) sys.stdout.write(status) sys.stdout.flush() _refresh_stats.start_time = None def _trigger_loop(): """ Waits for input on stdin and launches appropriate actions. """ is_posix = sys.platform != 'win32' old_count = len(workflow.pages) if is_posix: import select old_settings = termios.tcgetattr(sys.stdin) def data_available(): return (select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])) def read_char(): return sys.stdin.read(1) else: data_available = msvcrt.kbhit read_char = msvcrt.getch try: if is_posix: tty.setcbreak(sys.stdin.fileno()) while True: time.sleep(0.01) if len(workflow.pages) != old_count: old_count = len(workflow.pages) _refresh_stats() if not data_available(): continue char = read_char() if char in tuple(capture_keys) + ('r', ): # Capture or retake workflow.capture(retake=(char == 'r')) _refresh_stats() elif char == 'f': # Finish capturing break finally: if is_posix: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) if len(workflow.devices) not in (1, 2): raise DeviceException("Please connect and turn on one or two" " pre-configured devices! ({0} were" " found)".format(len(workflow.devices))) print( colorize("Found {0} devices!".format(len(workflow.devices)), colorama.Fore.GREEN)) if any(not x.target_page for x in workflow.devices): raise DeviceException("At least one of the devices has not been" " properly configured, please re-run the" " program with the \'configure\' option!") # Set up for capturing print("Setting up devices for capturing.") workflow.prepare_capture() print("({0}) capture | (r) retake last shot | (f) finish ".format( "/".join(capture_keys))) # Start trigger loop _trigger_loop() workflow.finish_capture()
def capture(config): path = config['path'].get() workflow = Workflow(config=config, path=path) workflow.on_created.send(workflow=workflow) capture_keys = workflow.config['core']['capture_keys'].as_str_seq() # Some closures def refresh_stats(): # Callback to print statistics if refresh_stats.start_time is not None: pages_per_hour = ((3600 / (time.time() - refresh_stats.start_time)) * workflow.pages_shot) else: pages_per_hour = 0.0 refresh_stats.start_time = time.time() status = ("\rShot {0: >3} pages [{1: >4.0f}/h] ".format( unicode(workflow.pages_shot), pages_per_hour)) sys.stdout.write(status) sys.stdout.flush() refresh_stats.start_time = None def trigger_loop(): is_posix = sys.platform != 'win32' old_count = workflow.pages_shot if is_posix: import select old_settings = termios.tcgetattr(sys.stdin) data_available = lambda: (select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])) read_char = lambda: sys.stdin.read(1) else: data_available = msvcrt.kbhit read_char = msvcrt.getch try: if is_posix: tty.setcbreak(sys.stdin.fileno()) while True: time.sleep(0.01) if workflow.pages_shot != old_count: old_count = workflow.pages_shot refresh_stats() if not data_available(): continue char = read_char() if char in tuple(capture_keys) + ('r', ): workflow.capture(retake=(char == 'r')) refresh_stats() elif char == 'f': break finally: if is_posix: termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings) if len(workflow.devices) != 2: raise DeviceException("Please connect and turn on two" " pre-configured devices! ({0} were" " found)".format(len(workflow.devices))) print( colorize("Found {0} devices!".format(len(workflow.devices)), colorama.Fore.GREEN)) if any(not x.target_page for x in workflow.devices): raise DeviceException("At least one of the devices has not been" " properly configured, please re-run the" " program with the \'configure\' option!") # Set up for capturing print("Setting up devices for capturing.") workflow.prepare_capture() print("({0}) capture | (r) retake last shot | (f) finish ".format( "/".join(capture_keys))) # Start trigger loop trigger_loop() workflow.finish_capture()