def receive_sa(self, msg): self.log.info("Received SA: %s", msg) hw_states = dict() num_local, local_states, num_nw, nw_states = msg.split(',') for offset, byte in enumerate(bytearray.fromhex(nw_states)): for i in range(8): num = Util.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 1)] = 1 else: hw_states[(num, 1)] = 0 for offset, byte in enumerate(bytearray.fromhex(local_states)): for i in range(8): num = Util.int_to_hex_string((offset * 8) + i) if byte & (2**i): hw_states[(num, 0)] = 1 else: hw_states[(num, 0)] = 0 self.hw_switch_data = hw_states
def pulse(self, milliseconds=None): """Pulses this driver. """ if not milliseconds: hex_ms_string = self.driver_settings['pulse_ms'] else: hex_ms_string = Util.int_to_hex_string(milliseconds) if self.autofire: cmd = (self.driver_settings['trigger_cmd'] + self.driver_settings['number'] + ',' + '01') if milliseconds: self.log.debug("Received command to pulse driver for %sms, but" "this driver is configured with an autofire rule" ", so that pulse value will be used instead.") else: cmd = (self.driver_settings['config_cmd'] + self.driver_settings['number'] + ',89,00,10,' + hex_ms_string + ',' + self.driver_settings['pwm1'] + ',00,00,' + self.driver_settings['recycle_ms']) self.log.debug("Sending Pulse Command: %s", cmd) self.send(cmd) self.check_auto()
def configure_led(self, config): if not self.rgb_connection: self.log.critical("A request was made to configure a FAST LED, " "but no connection to an LED processor is " "available") sys.exit() if not self.flag_led_tick_registered: self.machine.events.add_handler('timer_tick', self.update_leds) self.flag_led_tick_registered = True # if the LED number is in <channel> - <led> format, convert it to a # FAST hardware number if '-' in config['number_str']: num = config['number_str'].split('-') config['number'] = (int(num[0]) * 64) + int(num[1]) self.config['config_number_format'] = 'int' else: config['number'] = str(config['number']) if self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) this_fast_led = FASTDirectLED(config['number']) self.fast_leds.add(this_fast_led) return this_fast_led
def create_driver_settings(self, machine, pulse_ms=None, **kwargs): return_dict = dict() if pulse_ms is None: pulse_ms = machine.config['mpf']['default_pulse_ms'] return_dict['pulse_ms'] = Util.int_to_hex_string(pulse_ms) return_dict['pwm1'] = 'ff' return_dict['pwm2'] = 'ff' return_dict['recycle_ms'] = '00' return return_dict
def configure_matrixlight(self, config): if not self.net_connection: self.log.critical("A request was made to configure a FAST matrix " "light, but no connection to a NET processor is " "available") sys.exit() if self.machine_type == 'wpc': # translate number to FAST light num config['number'] = self.wpc_light_map.get( config['number_str'].upper()) elif self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) return (FASTMatrixLight(config['number'], self.net_connection.send), config['number'])
def configure_driver(self, config, device_type='coil'): if not self.net_connection: self.log.critical("A request was made to configure a FAST driver, " "but no connection to a NET processor is " "available") sys.exit() # If we have WPC driver boards, look up the driver number if self.machine_type == 'wpc': config['number'] = self.wpc_driver_map.get( config['number_str'].upper()) if ('connection' in config and config['connection'].lower() == 'network'): config['connection'] = 1 else: config['connection'] = 0 # local driver (default for WPC) # If we have FAST IO boards, we need to make sure we have hex strings elif self.machine_type == 'fast': if self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) # Now figure out the connection type if ('connection' in config and config['connection'].lower() == 'local'): config['connection'] = 0 else: config['connection'] = 1 # network driver (default for FAST) else: self.log.critical("Invalid machine type: {0{}}".format( self.machine_type)) sys.exit() return (FASTDriver(config, self.net_connection.send, self.machine), (config['number'], config['connection']))
def merge_driver_settings(self, pulse_ms=None, pwm_on_ms=None, pwm_off_ms=None, pulse_power=None, hold_power=None, pulse_power32=None, hold_power32=None, pulse_pwm_mask=None, hold_pwm_mask=None, recycle_ms=None, activation_time=None, **kwargs ): if pwm_on_ms: raise ValueError("The setting 'pwm_on_ms' is not valid with the " "FAST platform. Use a hold_power or hold_pwm_mask" " instead.") if pwm_off_ms: raise ValueError("The setting 'pwm_off_ms' is not valid with the " "FAST platform. Use a hold_power or hold_pwm_mask" " instead.") if pulse_power32: raise NotImplementedError('"pulse_power32" has not been ' 'implemented yet') if hold_power32: raise NotImplementedError('"hold_power32" has not been ' 'implemented yet') return_dict = dict() return_dict['pwm32'] = None if activation_time is not None: if activation_time > 25500: raise ValueError('Max FAST timed_hold time is 25.5s') # FAST activation times are ms * 100 return_dict['activation_time'] = str(activation_time / 100) if recycle_ms is not None: return_dict['recycle_ms'] = (Util.int_to_hex_string(recycle_ms)) if pulse_ms is not None: return_dict['pulse_ms'] = Util.int_to_hex_string(pulse_ms) if pulse_pwm_mask: pulse_pwm_mask = str(pulse_pwm_mask) if len(pulse_pwm_mask) == 32: return_dict['pwm1'] = Util.bin_str_to_hex_str(pulse_pwm_mask, 8) elif len(pulse_pwm_mask) == 8: return_dict['pwm1'] = Util.bin_str_to_hex_str(pulse_pwm_mask, 2) else: raise ValueError("pulse_pwm_mask must either be 8 or 32 bits") elif pulse_power32 is not None: return_dict['pwm32'] = Util.pwm32_to_hex_string(pulse_power32) elif pulse_power is not None: return_dict['pwm1'] = Util.pwm8_to_hex_string(pulse_power) if hold_pwm_mask: hold_pwm_mask = str(hold_pwm_mask) if len(hold_pwm_mask) == 32: return_dict['pwm2'] = Util.bin_str_to_hex_str(hold_pwm_mask, 8) elif len(hold_pwm_mask) == 8: return_dict['pwm2'] = Util.bin_str_to_hex_str(hold_pwm_mask, 2) else: raise ValueError("hold_pwm_mask must either be 8 or 32 bits") elif hold_power32 is not None: return_dict['pwm32'] = Util.pwm32_to_hex_string(hold_power32) elif hold_power is not None: return_dict['pwm2'] = Util.pwm8_to_hex_string(hold_power) return return_dict
def write_hw_rule(self, switch_obj, sw_activity, driver_obj, driver_action, disable_on_release=True, drive_now=False, **driver_settings_overrides): """Used to write (or update) a hardware rule to the FAST controller. *Hardware Rules* are used to configure the hardware controller to automatically change driver states based on switch changes. These rules are completely handled by the hardware (i.e. with no interaction from the Python game code). They're used for things that you want to happen fast, like firing coils when flipper buttons are pushed, slingshots, pop bumpers, etc. You can overwrite existing hardware rules at any time to change or remove them. Args: switch_obj: Which switch you're creating this rule for. The parameter is a reference to the switch object itself. sw_activity: Int which specifies whether this coil should fire when the switch becomes active (1) or inactive (0) driver_obj: Driver object this rule is being set for. driver_action: String 'pulse' or 'hold' which describe what action will be applied to this driver drive_now: Should the hardware check the state of the switches when this rule is first applied, and fire the coils if they should be? Typically this is True, especially with flippers because you want them to fire if the player is holding in the buttons when the machine enables the flippers (which is done via several calls to this method.) """ driver_settings = deepcopy(driver_obj.hw_driver.driver_settings) driver_settings.update(driver_obj.hw_driver.merge_driver_settings( **driver_settings_overrides)) self.log.debug("Setting HW Rule. Switch: %s, Switch_action: %s, Driver:" " %s, Driver settings: %s", switch_obj.name, sw_activity, driver_obj.name, driver_settings) control = 0x01 # Driver enabled if drive_now: control += 0x08 if sw_activity == 0: control += 0x10 control = Util.int_to_hex_string(int(control)) # todo need to implement disable_on_release if driver_action == 'pulse': mode = '10' # Mode 10 settings param1 = driver_settings['pulse_ms'] # initial pulse ms param2 = driver_settings['pwm1'] # intial pwm param3 = '00' # pulse 2 time param4 = '00' # pulse 2 pwm param5 = driver_settings['recycle_ms'] # recycle ms elif driver_action == 'hold': mode = '18' # Mode 18 settings param1 = driver_settings['pulse_ms'] # intiial pulse ms param2 = driver_settings['pwm1'] # intial pwm param3 = driver_settings['pwm2'] # hold pwm param4 = driver_settings['recycle_ms'] # recycle ms param5 = '00' # not used with Mode 18 elif driver_action == 'timed_hold': # fast hold time is ms*100 hold_value = driver_settings['activation_time'] mode = '70' # Mode 70 settings param1 = driver_settings['pulse_ms'] # intiial pulse ms param2 = driver_settings['pwm1'] # intial pwm param3 = hold_value # hold time param4 = driver_settings['pwm2'] # hold pwm param5 = driver_settings['recycle_ms'] # recycle ms else: raise ValueError("Invalid driver action: '%s'. Expected 'hold', " "'timed_hold', or 'pulse'" % (driver_action)) self.hw_rules[driver_obj] = {'mode': mode, 'param1': param1, 'param2': param2, 'param3': param3, 'param4': param4, 'param5': param5, 'switch': switch_obj.number} cmd = (driver_settings['config_cmd'] + driver_obj.number[0] + ',' + control + ',' + switch_obj.number[0] + ',' + mode + ',' + param1 + ',' + param2 + ',' + param3 + ',' + param4 + ',' + param5) driver_obj.autofire = cmd self.log.debug("Writing hardware rule: %s", cmd) self.net_connection.send(cmd)
def configure_switch(self, config): """Configures the switch object for a FAST Pinball controller. FAST Controllers support two types of switches: `local` and `network`. Local switches are switches that are connected to the FAST controller board itself, and network switches are those connected to a FAST I/O board. MPF needs to know which type of switch is this is. You can specify the switch's connection type in the config file via the ``connection:`` setting (either ``local`` or ``network``). If a connection type is not specified, this method will use some intelligence to try to figure out which default should be used. If the DriverBoard type is ``fast``, then it assumes the default is ``network``. If it's anything else (``wpc``, ``system11``, ``bally``, etc.) then it assumes the connection type is ``local``. Connection types can be mixed and matched in the same machine. """ if not self.net_connection: self.log.critical("A request was made to configure a FAST switch, " "but no connection to a NET processor is " "available") sys.exit() if self.machine_type == 'wpc': # translate switch number to FAST switch config['number'] = self.wpc_switch_map.get( config['number_str'].upper()) if 'connection' not in config: config['connection'] = 0 # local switch (default for WPC) else: config['connection'] = 1 # network switch elif self.machine_type == 'fast': if 'connection' not in config: config['connection'] = 1 # network switch (default for FAST) else: config['connection'] = 0 # local switch if self.config['config_number_format'] == 'int': config['number'] = Util.int_to_hex_string(config['number']) else: config['number'] = Util.normalize_hex_string(config['number']) # convert the switch number into a tuple which is: # (switch number, connection) config['number'] = (config['number'], config['connection']) if not config['debounce_open']: config['debounce_open'] = self.config['default_debounce_open'] if not config['debounce_close']: config['debounce_close'] = self.config['default_debounce_close'] self.log.debug("FAST Switch hardware tuple: %s", config['number']) switch = FASTSwitch(number=config['number'], debounce_open=config['debounce_open'], debounce_close=config['debounce_close'], sender=self.net_connection.send) return switch, config['number']