def device_class_init(cls, machine: MachineController): """Initialise lights. Args: machine: MachineController object """ cls.machine = machine cls.lights_to_fade = set() cls.lights_to_update = set() machine.validate_machine_config_section('matrix_light_settings') cls._updater_task = machine.clock.schedule_interval( cls.update_matrix_lights, 1 / machine.config['mpf']['default_matrix_light_hw_update_hz']) machine.mode_controller.register_stop_method(cls.mode_stop) machine.settings.add_setting( SettingEntry("brightness", "Brightness", 100, "brightness", 1.0, { 0.25: "25%", 0.5: "50%", 0.75: "75%", 1.0: "100% (default)" }))
class Command(object): """Performs hardware operations.""" def __init__(self, mpf_path, machine_path, args): """Parse args.""" self.mpf_path = mpf_path self.machine_path = machine_path self.args = args self.mpf = MachineController( self.mpf_path, self.machine_path, { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "create_config_cache": False, "force_platform": False, "text_ui": False }) self.mpf.initialise_mpf() def scan(self): """Scan hardware.""" for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.get_info_string()) print("---------")
def __init__(self, args, path): """Parse args.""" command_name = args.pop(1) super().__init__(args=args, path=path) machine_path, remaining_args = self.parse_args() self.machine_path = machine_path self.args = remaining_args config_loader = YamlMultifileConfigLoader(machine_path, ["config.yaml"], True, False) config = config_loader.load_mpf_config() self.mpf = MachineController( { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(self.mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }, config) self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") method = getattr(self, command_name) method()
def __init__(self, args, path): """Parse args.""" command_name = args.pop(1) super().__init__(args=args, path=path) machine_path, remaining_args = self.parse_args() self.machine_path = machine_path self.args = remaining_args config_loader = YamlMultifileConfigLoader(machine_path, ["config.yaml"], True, False) config = config_loader.load_mpf_config() self.mpf = MachineController( { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(self.mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }, config) method = getattr(self, command_name) method()
class Command(MpfCommandLineParser): """Performs hardware operations.""" def __init__(self, args, path): """Parse args.""" command_name = args.pop(1) super().__init__(args=args, path=path) machine_path, remaining_args = self.parse_args() self.machine_path = machine_path self.args = remaining_args config_loader = YamlMultifileConfigLoader(machine_path, ["config.yaml"], True, False) config = config_loader.load_mpf_config() self.mpf = MachineController( { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(self.mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }, config) self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") method = getattr(self, command_name) method() def scan(self): """Scan hardware.""" for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.get_info_string()) print("---------") self.mpf.shutdown() def firmware_update(self): """Upgrade firmware of platforms.""" for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.update_firmware()) print("---------") self.mpf.shutdown()
class Command: """Runs the mpf game.""" # pylint: disable-msg=too-many-locals,too-many-statements def __init__(self, mpf_path, machine_path, args): """Generate wiring for game from MPL file.""" del mpf_path self.machine = None self._sigint_count = 0 parser = argparse.ArgumentParser(description='Generates wiring .yaml file') parser.add_argument("-c", action="store", dest="configfile", default="config.yaml", metavar='config_file', help="The name of a config file to load. Default " "is " "config.yaml. Multiple files can be used " "via a comma-" "separated list (no spaces between)") self.args = parser.parse_args(args) self.args.configfile = Util.string_to_event_list(self.args.configfile) # To initialize and check machine, load it onto the virtual platform - we have to use the virtual platform # because it would be helpful to be able to calculate wiring before setting up physical hardware. self.args.__dict__["production"] = False self.args.__dict__["force_platform"] = "smart_virtual" self.args.__dict__["text_ui"] = False self.args.__dict__["bcp"] = False config_loader = YamlMultifileConfigLoader(machine_path, self.args.configfile, False, False) config = config_loader.load_mpf_config() # print(config.get_machine_config()) self.machine = MachineController(vars(self.args), config) self.machine.initialise_mpf() result = wire(self.machine) yaml = YAML() yaml.default_flow_style = False f = open("wiring.yaml", "w", encoding="utf-8") yaml.dump(result, f) f.close()
def __init__(self, mpf_path, machine_path, args): """Parse args.""" self.mpf_path = mpf_path self.machine_path = machine_path self.args = args self.mpf = MachineController( self.mpf_path, self.machine_path, { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "create_config_cache": False, "force_platform": False, "text_ui": False }) self.mpf.initialise_mpf()
def __init__(self, mpf_path, machine_path, args): """Parse args.""" self.mpf_path = mpf_path self.machine_path = machine_path self.args = args self.mpf = MachineController( self.mpf_path, self.machine_path, { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }) self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!")
class Command(object): """Performs hardware operations.""" def __init__(self, mpf_path, machine_path, args): """Parse args.""" self.mpf_path = mpf_path self.machine_path = machine_path self.args = args self.mpf = MachineController( self.mpf_path, self.machine_path, { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }) self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") def scan(self): """Scan hardware.""" for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.get_info_string()) print("---------") self.mpf.shutdown() def firmware_update(self): """Upgrade firmware of platforms.""" for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.update_firmware()) print("---------") self.mpf.shutdown()
def device_class_init(cls, machine: MachineController): """Initialise all LEDs. Args: machine: MachineController which is used """ cls.machine = machine cls.leds_to_fade = set() cls.leds_to_update = set() machine.validate_machine_config_section('led_settings') if machine.config['led_settings']['color_correction_profiles'] is None: machine.config['led_settings']['color_correction_profiles'] = ( dict()) # Generate and add color correction profiles to the machine machine.led_color_correction_profiles = dict() # Create the default color correction profile and add it to the machine default_profile = RGBColorCorrectionProfile.default() machine.led_color_correction_profiles['default'] = default_profile # Add any user-defined profiles specified in the machine config file for profile_name, profile_parameters in ( machine.config['led_settings'] ['color_correction_profiles'].items()): machine.config_validator.validate_config( 'color_correction_profile', machine.config['led_settings'] ['color_correction_profiles'][profile_name], profile_parameters) profile = RGBColorCorrectionProfile(profile_name) profile.generate_from_parameters( gamma=profile_parameters['gamma'], whitepoint=profile_parameters['whitepoint'], linear_slope=profile_parameters['linear_slope'], linear_cutoff=profile_parameters['linear_cutoff']) machine.led_color_correction_profiles[profile_name] = profile # schedule the single machine-wide update to write the current led of # each LED to the hardware cls._updater_task = machine.clock.schedule_interval( cls.update_leds, 1 / machine.config['mpf']['default_led_hw_update_hz']) machine.mode_controller.register_stop_method(cls.mode_stop) machine.settings.add_setting( SettingEntry("brightness", "Brightness", 100, "brightness", 1.0, { 0.25: "25%", 0.5: "50%", 0.75: "75%", 1.0: "100% (default)" }))
def __init__(self, mpf_path, machine_path, args): """Run mpf game.""" signal.signal(signal.SIGINT, self.exit) parser = argparse.ArgumentParser( description='Starts the MPF game engine') parser.add_argument("-a", action="store_true", dest="no_load_cache", help="Forces the config to be loaded from files " "and not cache") parser.add_argument("-A", action="store_false", dest="create_config_cache", help="Does not create the cache config files") parser.add_argument("-b", action="store_false", dest="bcp", default=True, help="Runs MPF without making a connection " "attempt to a " "BCP Server") parser.add_argument("-c", action="store", dest="configfile", default="config.yaml", metavar='config_file', help="The name of a config file to load. Default " "is " "config.yaml. Multiple files can be used " "via a comma-" "separated list (no spaces between)") parser.add_argument("-C", action="store", dest="mpfconfigfile", default=os.path.join(mpf_path, "mpfconfig.yaml"), metavar='config_file', help="The MPF framework default config file. " "Default is " "mpf/mpfconfig.yaml") parser.add_argument("-f", action="store_true", dest="force_assets_load", default=False, help="Load all assets upon startup. Useful for " "ensuring all assets are set up properly " "during development.") parser.add_argument( "-l", action="store", dest="logfile", metavar='file_name', default=os.path.join( "logs", datetime.now().strftime("%Y-%m-%d-%H-%M-%S-mpf-" + socket.gethostname() + ".log")), help="The name (and path) of the log file") parser.add_argument("-p", action="store_true", dest="pause", default=False, help="Pause the terminal window on exit. Useful " "when launching in a separate window so you can " "see any errors before the window closes.") parser.add_argument( "-P", action="store_true", dest="production", default=False, help= "Production mode. Will suppress errors, wait for hardware on start and " "try to exit when startup fails. Run this inside a loop.") parser.add_argument("-t", action="store_false", dest='text_ui', default=True, help="Use the ASCII test-based UI") parser.add_argument("-v", action="store_const", dest="loglevel", const=logging.DEBUG, default=15, help="Enables verbose logging to the" " log file") parser.add_argument( "-V", action="store_const", dest="consoleloglevel", const=logging.DEBUG, default=logging.INFO, help="Enables verbose logging to the console. DO " "NOTE: On Windows platforms you must also use -v for " "this to work.") parser.add_argument("-x", action="store_const", dest="force_platform", const='virtual', help="Forces the virtual platform to be " "used for all devices") parser.add_argument("--syslog_address", action="store", dest="syslog_address", help="Log to the specified syslog address. This " "can be a domain socket such as /dev/og on " "Linux or /var/run/syslog on Mac. " "Alternatively, you an specify host:port for " "remote logging over UDP.") parser.add_argument("-X", action="store_const", dest="force_platform", const='smart_virtual', help="Forces the smart virtual platform to be " "used for all" " devices") # The following are just included for full compatibility with mc # which is needed when using "mpf both". parser.add_argument("-L", action="store", dest="mc_file_name", metavar='mc_file_name', default=None, help=argparse.SUPPRESS) self.args = parser.parse_args(args) self.args.configfile = Util.string_to_list(self.args.configfile) # Configure logging. Creates a logfile and logs to the console. # Formatting options are documented here: # https://docs.python.org/2.7/library/logging.html#logrecord-attributes try: os.makedirs(os.path.join(machine_path, 'logs')) except OSError as exception: if exception.errno != errno.EEXIST: raise full_logfile_path = os.path.join(machine_path, self.args.logfile) try: os.remove(full_logfile_path) except OSError: pass if self.args.text_ui: console_log = logging.NullHandler() console_log.setLevel(logging.ERROR) else: console_log = logging.StreamHandler() console_log.setLevel(self.args.consoleloglevel) # tell the handler to use this format console_log.setFormatter( logging.Formatter('%(levelname)s : %(name)s : %(message)s')) # initialise async handler for console console_log_queue = Queue() console_queue_handler = QueueHandler(console_log_queue) self.console_queue_listener = logging.handlers.QueueListener( console_log_queue, console_log) self.console_queue_listener.start() # initialise file log file_log = logging.FileHandler(full_logfile_path) file_log.setFormatter( logging.Formatter( '%(asctime)s : %(levelname)s : %(name)s : %(message)s')) # initialise async handler for file log file_log_queue = Queue() file_queue_handler = QueueHandler(file_log_queue) self.file_queue_listener = logging.handlers.QueueListener( file_log_queue, file_log) self.file_queue_listener.start() # add loggers logger = logging.getLogger() logger.addHandler(console_queue_handler) logger.addHandler(file_queue_handler) logger.setLevel(self.args.loglevel) if self.args.syslog_address: try: host, port = self.args.syslog_address.split(":") except ValueError: syslog_logger = SysLogHandler(self.args.syslog_address) else: syslog_logger = SysLogHandler((host, int(port))) logger.addHandler(syslog_logger) try: MachineController(mpf_path, machine_path, vars(self.args)).run() logging.info("MPF run loop ended.") self.exit() # pylint: disable-msg=broad-except except Exception as e: self.exit(exception=e)
class Command: """Runs the mpf game.""" # pylint: disable-msg=too-many-locals,too-many-statements def __init__(self, mpf_path, machine_path, args): """Run mpf game.""" del mpf_path self.machine = None self._sigint_count = 0 parser = argparse.ArgumentParser( description='Starts the MPF game engine') parser.add_argument("-a", action="store_true", dest="no_load_cache", help="Forces the config to be loaded from files " "and not cache") parser.add_argument("-A", action="store_false", dest="create_config_cache", help="Does not create the cache config files") parser.add_argument("-b", action="store_false", dest="bcp", default=True, help="Runs MPF without making a connection " "attempt to a " "BCP Server") parser.add_argument("-c", action="store", dest="configfile", default="config.yaml", metavar='config_file', help="The name of a config file to load. Default " "is " "config.yaml. Multiple files can be used " "via a comma-" "separated list (no spaces between)") parser.add_argument("-f", action="store_true", dest="force_assets_load", default=False, help="Load all assets upon startup. Useful for " "ensuring all assets are set up properly " "during development.") parser.add_argument("--json-logging", action="store_true", dest="jsonlogging", default=False, help="Enables json logging to file. ") parser.add_argument( "-l", action="store", dest="logfile", metavar='file_name', default=os.path.join( "logs", datetime.now().strftime("%Y-%m-%d-%H-%M-%S-mpf-" + socket.gethostname() + ".log")), help="The name (and path) of the log file") parser.add_argument("-p", action="store_true", dest="pause", default=False, help="Pause the terminal window on exit. Useful " "when launching in a separate window so you can " "see any errors before the window closes.") parser.add_argument( "-P", action="store_true", dest="production", default=False, help= "Production mode. Will suppress errors, wait for hardware on start and " "try to exit when startup fails. Run this inside a loop.") parser.add_argument("-t", action="store_false", dest='text_ui', default=True, help="Use the ASCII test-based UI") parser.add_argument("-v", action="store_const", dest="loglevel", const=logging.DEBUG, default=15, help="Enables verbose logging to the" " log file") parser.add_argument( "-V", action="store_const", dest="consoleloglevel", const=logging.DEBUG, default=logging.INFO, help="Enables verbose logging to the console. DO " "NOTE: On Windows platforms you must also use -v for " "this to work.") parser.add_argument("-x", action="store_const", dest="force_platform", const='virtual', help="Forces the virtual platform to be " "used for all devices") parser.add_argument("--vpx", action="store_const", dest="force_platform", const='virtual_pinball', help="Forces the virtual_pinball platform to be " "used for all devices") parser.add_argument("--syslog_address", action="store", dest="syslog_address", help="Log to the specified syslog address. This " "can be a domain socket such as /dev/og on " "Linux or /var/run/syslog on Mac. " "Alternatively, you an specify host:port for " "remote logging over UDP.") parser.add_argument("-X", action="store_const", dest="force_platform", const='smart_virtual', help="Forces the smart virtual platform to be " "used for all" " devices") # The following are just included for full compatibility with mc # which is needed when using "mpf both". parser.add_argument("-L", action="store", dest="mc_file_name", metavar='mc_file_name', default=None, help=argparse.SUPPRESS) parser.add_argument("--no-sound", action="store_true", dest="no_sound", default=False) self.args = parser.parse_args(args) self.args.configfile = Util.string_to_event_list(self.args.configfile) # Configure logging. Creates a logfile and logs to the console. # Formatting options are documented here: # https://docs.python.org/2.7/library/logging.html#logrecord-attributes try: os.makedirs(os.path.join(machine_path, 'logs')) except OSError as exception: if exception.errno != errno.EEXIST: raise full_logfile_path = os.path.join(machine_path, self.args.logfile) try: os.remove(full_logfile_path) except OSError: pass if self.args.text_ui: console_log = logging.NullHandler() console_log.setLevel(logging.ERROR) else: console_log = logging.StreamHandler() console_log.setLevel(self.args.consoleloglevel) # tell the handler to use this format console_log.setFormatter( logging.Formatter('%(levelname)s : %(name)s : %(message)s')) # initialise async handler for console console_log_queue = Queue() console_queue_handler = QueueHandler(console_log_queue) self.console_queue_listener = logging.handlers.QueueListener( console_log_queue, console_log) self.console_queue_listener.start() # initialise file log file_log = logging.FileHandler(full_logfile_path) if self.args.jsonlogging: formatter = JSONFormatter() else: formatter = logging.Formatter( '%(asctime)s : %(levelname)s : %(name)s : %(message)s') file_log.setFormatter(formatter) # initialise async handler for file log file_log_queue = Queue() file_queue_handler = QueueHandler(file_log_queue) self.file_queue_listener = logging.handlers.QueueListener( file_log_queue, file_log) self.file_queue_listener.start() # add loggers logger = logging.getLogger() logger.addHandler(console_queue_handler) logger.addHandler(file_queue_handler) logger.setLevel(self.args.loglevel) logger.info("Loading config.") if self.args.syslog_address: try: host, port = self.args.syslog_address.split(":") except ValueError: syslog_logger = SysLogHandler(self.args.syslog_address) else: syslog_logger = SysLogHandler((host, int(port))) logger.addHandler(syslog_logger) signal.signal(signal.SIGINT, self.sigint_handler) if not self.args.production: config_loader = YamlMultifileConfigLoader( machine_path, self.args.configfile, not self.args.no_load_cache, self.args.create_config_cache) else: config_loader = ProductionConfigLoader(machine_path) try: config = config_loader.load_mpf_config() except ConfigFileError as e: print("Error while parsing config: {}", e) report_crash(e, "config_parsing", {}) self.exit() try: self.machine = MachineController(vars(self.args), config) self.machine.add_crash_handler(self.restore_logger) self.machine.run() logging.info("MPF run loop ended.") self.exit() # pylint: disable-msg=broad-except except Exception as e: self.exit(exception=e) def sigint_handler(self, signum=None, frame=None): """Handle SIGINT.""" del signum, frame self._sigint_count += 1 if self._sigint_count > 1: self.exit("Received second SIGINT. Will exit ungracefully!") elif self.machine: self.machine.stop("SIGINT or keyboard interrupt") else: self.exit("Shutdown because of SIGINT or keyboard interrupt.") def restore_logger(self): """Restore logger.""" if self.args.text_ui: # Re-enable console logging logger = logging.getLogger() logger.addHandler(logging.StreamHandler()) def exit(self, exception=None): """Handle MPF exit from either a clean shutdown or from a crash. Cleanly shuts down logging and restores the console window if the Text UI option is used. """ if exception: logging.exception(exception) logging.shutdown() self.console_queue_listener.stop() self.file_queue_listener.stop() if self.args.pause: input('Press ENTER to continue...') # nosec sys.exit()
import logging import sys from mpf.core.config_loader import YamlMultifileConfigLoader from mpf.core.machine import MachineController machine_path = sys.argv[1] config_loader = YamlMultifileConfigLoader(machine_path, ["config.yaml"], False, False) config = config_loader.load_mpf_config() options = { 'force_platform': 'smart_virtual', 'production': False, 'mpfconfigfile': ["mpfconfig.yaml"], 'configfile': ["config.yaml"], 'debug': True, 'bcp': True, 'no_load_cache': False, 'create_config_cache': True, 'text_ui': False, 'consoleloglevel': logging.DEBUG, } logging.basicConfig(level=logging.DEBUG) machine = MachineController(options, config) machine.run()
def __init__(self, mpf_path, machine_path, args): """Run mpf game.""" parser = argparse.ArgumentParser( description='Starts the MPF game engine') parser.add_argument("-c", action="store", dest="configfile", default="config", metavar='config_file', help="The name of a config file to load. Default " "is " "config.yaml. Multiple files can be used " "via a comma-" "separated list (no spaces between)") parser.add_argument("-v", action="store_const", dest="loglevel", const=logging.DEBUG, default=logging.INFO, help="Enables verbose logging to the" " log file") parser.add_argument("-V", action="store_true", dest="consoleloglevel", default=logging.INFO, help="Enables verbose logging to the console. Do " "NOT on " "Windows platforms. Must also use -v for " "this to work.") parser.add_argument("-x", action="store_const", dest="force_platform", const='virtual', help="Forces the virtual platform to be " "used for all devices") parser.add_argument("-a", action="store_true", dest="no_load_cache", help="Forces the config to be loaded from files " "and not " "cache") parser.add_argument("-A", action="store_false", dest="create_config_cache", help="Does not create the cache config files") parser.add_argument("-X", action="store_const", dest="force_platform", const='smart_virtual', help="Forces the smart virtual platform to be " "used for all" " devices") parser.add_argument("-b", action="store_false", dest="bcp", default=True, help="Runs MPF without making a connection " "attempt to a " "BCP Server") parser.add_argument( "-l", action="store", dest="logfile", metavar='file_name', default=os.path.join( "logs", datetime.now().strftime("%Y-%m-%d-%H-%M-%S-mpf-" + socket.gethostname() + ".log")), help="The name (and path) of the log file") parser.add_argument("-C", action="store", dest="mpfconfigfile", default=os.path.join(mpf_path, "mpfconfig.yaml"), metavar='config_file', help="The MPF framework default config file. " "Default is " "mpf/mpfconfig.yaml") parser.add_argument("-p", action="store_true", dest="pause", default=False, help="Pause the terminal window on exit. Useful " "when launching in a separate window so you can " "see any errors before the window closes.") parser.add_argument("-f", action="store_true", dest="force_assets_load", default=False, help="Load all assets upon startup. Useful for " "ensuring all assets are set up properly " "during development.") args = parser.parse_args(args) args.configfile = Util.string_to_list(args.configfile) # Configure logging. Creates a logfile and logs to the console. # Formatting options are documented here: # https://docs.python.org/2.7/library/logging.html#logrecord-attributes try: os.makedirs(os.path.join(machine_path, 'logs')) except OSError as exception: if exception.errno != errno.EEXIST: raise logging.basicConfig(level=args.loglevel, format='%(asctime)s : %(levelname)s : %(name)s : ' '%(message)s', filename=os.path.join(machine_path, args.logfile), filemode='w') # define a Handler which writes INFO messages or higher to the # sys.stderr console = logging.StreamHandler() console.setLevel(args.consoleloglevel) # set a format which is simpler for console use formatter = logging.Formatter('%(levelname)s : %(name)s : %(message)s') # tell the handler to use this format console.setFormatter(formatter) # add the handler to the root logger logging.getLogger('').addHandler(console) try: MachineController(mpf_path, machine_path, vars(args)).run() logging.info("MPF run loop ended.") # pylint: disable-msg=broad-except except Exception as e: logging.exception(e) if args.pause: input('Press ENTER to continue...') sys.exit()
class Command(MpfCommandLineParser): """Performs hardware operations.""" def __init__(self, args, path): """Parse args.""" command_name = args.pop(1) super().__init__(args=args, path=path) machine_path, remaining_args = self.parse_args() self.machine_path = machine_path self.args = remaining_args config_loader = YamlMultifileConfigLoader(machine_path, ["config.yaml"], True, False) config = config_loader.load_mpf_config() self.mpf = MachineController( { "bcp": False, "no_load_cache": False, "mpfconfigfile": os.path.join(self.mpf_path, "mpfconfig.yaml"), "configfile": ["config"], "production": False, "create_config_cache": False, "force_platform": False, "text_ui": False }, config) method = getattr(self, command_name) method() def scan(self): """Scan hardware.""" self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.get_info_string()) print("---------") self.mpf.shutdown() def firmware_update(self): """Upgrade firmware of platforms.""" self.mpf.clock.loop.run_until_complete( self.mpf.initialise_core_and_hardware()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") for name, platform in self.mpf.hardware_platforms.items(): print("{}:".format(name)) print(platform.update_firmware()) print("---------") self.mpf.shutdown() # pylint: disable-msg=too-many-locals def _test_repeated_pulses_with_rule(self, config, pulse_ms, pause_min, pause_max): latency = [] rule_latency = [] pulse_duration = [] rule_pulse_duration = [] timeouts = 0 config["flipper"].enable() for _ in range(100): # measure coil -> input latency pulse_start = time.time() config["coil1"].pulse(pulse_ms=pulse_ms) try: self.mpf.clock.loop.run_until_complete( asyncio.wait_for( self.mpf.switch_controller.wait_for_switch( config["switch1"], state=1, only_on_change=False), timeout=.5)) switch_active = time.time() self.mpf.clock.loop.run_until_complete( asyncio.wait_for( self.mpf.switch_controller.wait_for_switch( config["switch2"], state=1, only_on_change=False), timeout=.5)) switch2_active = time.time() self.mpf.clock.loop.run_until_complete( asyncio.wait_for( self.mpf.switch_controller.wait_for_switch( config["switch1"], state=0, only_on_change=False), timeout=.5)) switch_inactive = time.time() self.mpf.clock.loop.run_until_complete( asyncio.wait_for( self.mpf.switch_controller.wait_for_switch( config["switch2"], state=0, only_on_change=False), timeout=.5)) switch2_inactive = time.time() except asyncio.TimeoutError: print( "WARNING: Ran into timeout while waiting. Check your setup!" ) timeouts += 1 continue self.mpf.clock.loop.run_until_complete( asyncio.sleep( random.uniform(pause_min * 0.001, pause_max * 0.001))) latency.append((switch_active - pulse_start) * 1000) rule_latency.append((switch2_active - switch_active) * 1000) pulse_duration.append((switch_inactive - switch_active) * 1000) rule_pulse_duration.append( (switch2_inactive - switch2_active) * 1000) print( "----------------------------------------------------------------------------------------" ) print("Pulse duration: {}ms Pause: {}ms to {}ms".format( pulse_ms, pause_min, pause_max)) print( "Latency mean: {:.2f} median: {:.2f} min: {:.2f} max: {:.2f} stdev: {:.2f} variance: {:.2f}" .format(statistics.mean(latency), statistics.median(latency), min(latency), max(latency), statistics.stdev(latency), statistics.variance(latency))) print( "Rule Latency mean: {:.2f} median: {:.2f} min: {:.2f} max: {:.2f} stdev: {:.2f} variance: {:.2f}" .format(statistics.mean(rule_latency), statistics.median(rule_latency), min(rule_latency), max(rule_latency), statistics.stdev(rule_latency), statistics.variance(rule_latency))) print( "Pulse duration measured mean: {:.2f} median: {:.2f} min: {:.2f} max: {:.2f} stdev: {:.2f} " "variance: {:.2f}".format(statistics.mean(pulse_duration), statistics.median(pulse_duration), min(pulse_duration), max(pulse_duration), statistics.stdev(pulse_duration), statistics.variance(pulse_duration))) print( "Rule Pulse duration measured mean: {:.2f} median: {:.2f} min: {:.2f} max: {:.2f} stdev: {:.2f} " "variance: {:.2f}".format( statistics.mean(rule_pulse_duration), statistics.median(rule_pulse_duration), min(rule_pulse_duration), max(rule_pulse_duration), statistics.stdev(rule_pulse_duration), statistics.variance(rule_pulse_duration))) if timeouts: print( "Warning: Experienced {} timeouts during benchmark. Check your setup!" .format(timeouts)) print( "----------------------------------------------------------------------------------------" ) print() config["flipper"].disable() def benchmark(self): """Benchmark hardware.""" self.mpf.clock.loop.run_until_complete(self.mpf.initialise()) if self.mpf.thread_stopper.is_set(): raise AssertionError("Initialisation failed!") print(self.mpf.switches, self.mpf.coils) config = self.mpf.config_validator.validate_config( "hardware_benchmark", self.mpf.config.get("hardware_benchmark", {})) print( "1. Please confirm that you connected driver \"{}\" to switch \"{}\" and " "driver \"{}\" to switch \"{}\"".format(config["coil1"], config["switch1"], config["coil2"], config["switch2"])) print("2. Turn off high voltage!") print( "3. Hardware test will repeatedly pulse driver \"{}\" and \"{}\". " "Make sure they are not connected to coils and cannot cause any harm." .format(config["coil1"], config["coil2"])) print("4. Turn off high voltage! (seriously)") print("") input_text = input( "I am certain and know what I am doing (type YES if you are certain): " ) if input_text != "YES": print("Wrong input. Exiting!") self.mpf.shutdown() return input_text = input( "I did turn off high voltage (type HIGH VOLTAGE IS OFF): ") if input_text != "HIGH VOLTAGE IS OFF": print("Wrong input. Exiting!") self.mpf.shutdown() return print() print("This will take a few seconds. Please standby!") print() if config["flipper"].config["main_coil"] != config["coil2"]: print("Main_coil on flipper {} should be {} but is {}.".format( config["flipper"], config["coil2"], config["flipper"].config["main_coil"])) self.mpf.shutdown() return if config["flipper"].config["activation_switch"] != config["switch1"]: print("Activation_switch on flipper {} should be {} but is {}.". format(config["flipper"], config["switch1"], config["flipper"].config["activation_switch"])) self.mpf.shutdown() return if config["switch1"].state != 0: print("Switch {} should be inactive but is active.".format( config["switch1"])) self.mpf.shutdown() return if config["switch2"].state != 0: print("Switch {} should be inactive but is active.".format( config["switch2"])) self.mpf.shutdown() return # let the platform settle self.mpf.clock.loop.run_until_complete(asyncio.sleep(.5)) self._test_repeated_pulses_with_rule(config, 53, 50, 100) self.mpf.clock.loop.run_until_complete(asyncio.sleep(.5)) self._test_repeated_pulses_with_rule(config, 23, 5, 20) self.mpf.clock.loop.run_until_complete(asyncio.sleep(.5)) self._test_repeated_pulses_with_rule(config, 13, 1, 2) self.mpf.shutdown()