Skip to content

Commit

Permalink
Merge pull request #1848 from missionpinball/080-backport-dev
Browse files Browse the repository at this point in the history
Backport MPF 0.80 Updates to (dev) 0.57
  • Loading branch information
avanwinkle authored Oct 29, 2024
2 parents 6a2859a + c05db9d commit 811bb24
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 54 deletions.
20 changes: 12 additions & 8 deletions mpf/core/bcp/bcp_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,10 @@ def remove_registered_trigger_event_for_client(self, client, event):
if not self.machine.bcp.transport.get_transports_for_handler(event):
self.machine.events.remove_handler_by_event(event=event, handler=self.bcp_trigger)

async def _bcp_receive_set_machine_var(self, client, name, value):
async def _bcp_receive_set_machine_var(self, client, name, value, persist=False):
"""Set machine var via bcp."""
del client
self.machine.variables.set_machine_var(name, value)
self.machine.variables.set_machine_var(name, value, persist)
# document variables injected by MC
'''machine_var: mc_version
Expand Down Expand Up @@ -458,8 +458,8 @@ def _monitor_machine_vars(self, client):
self.machine.register_monitor('machine_vars', self._machine_var_change)

# Send initial machine variable values
for s in ("standard", "feature", "game", "coin"):
self._send_machine_vars(client, setting_type=s)
self._send_machine_vars(client)
self._send_machine_settings(client)

# Establish handler for machine variable changes
self.machine.bcp.transport.add_handler_to_transport("_machine_vars", client)
Expand All @@ -471,10 +471,14 @@ def _monitor_machine_vars_stop(self, client):
if not self.machine.bcp.transport.get_transports_for_handler("_machine_vars"):
self.machine.machine_var_monitor = False

def _send_machine_vars(self, client, setting_type=None):
self.machine.bcp.transport.send_to_client(
client, bcp_command='settings',
settings=Util.convert_to_simply_type(self.machine.settings.get_settings(setting_type)))
def _send_machine_settings(self, client, setting_type=None):
settings = [setting_type] if setting_type else ["standard", "feature", "game", "coin", "hw_volume"]
for s in settings:
self.machine.bcp.transport.send_to_client(
client, bcp_command='settings',
settings=Util.convert_to_simply_type(self.machine.settings.get_settings(s)))

def _send_machine_vars(self, client):
for var_name, settings in self.machine.variables.machine_vars.items():
self.machine.bcp.transport.send_to_client(client, bcp_command='machine_variable',
name=var_name,
Expand Down
8 changes: 8 additions & 0 deletions mpf/core/machine_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ def load_machine_vars(self, machine_var_data_manager: DataManager, current_time)
desc: Architecture of your machine (32bit/64bit).
'''
self.set_machine_var(name="log_file_path", value=self.machine.options.get('full_logfile_path', ""))
'''machine_var: log_file_path
desc: Absolute path of the file log for the current running game.
'''

def __getitem__(self, key):
"""Allow the user to access a machine variable with []. This would be used is machine.variables["var_name"]."""
Expand Down Expand Up @@ -177,6 +182,9 @@ def set_machine_var(self, name: str, value: Any, persist=False) -> None:
----
name: String name of the variable you're setting the value for.
value: The value you're setting. This can be any Type.
persist: Whether to persist this machine var to disk. Only
applies to new/unconfigured vars; vars defined in the
machine_vars config will use their config setting.
"""
if name not in self.machine_vars:
self.configure_machine_var(name=name, persist=persist)
Expand Down
8 changes: 4 additions & 4 deletions mpf/devices/shot_group.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def device_loaded_in_mode(self, mode: Mode, player: Player):
self.rotation_pattern = deque(self.profile.config['rotation_pattern'])
self.rotation_enabled = not self.config['enable_rotation_events']
for shot in self.config['shots']:
self.machine.events.add_handler("{}_hit".format(shot.name), self._hit)
self.machine.events.add_handler("{}_hit".format(shot.name), self._hit, shot=shot.name)
self.machine.events.add_handler("player_shot_{}".format(shot.name), self._check_for_complete)

def device_removed_from_mode(self, mode):
Expand Down Expand Up @@ -145,7 +145,7 @@ def restart(self):
for shot in self.config['shots']:
shot.restart()

def _hit(self, advancing, **kwargs):
def _hit(self, advancing, shot, **kwargs):
"""One of the member shots in this shot group was hit.
Args:
Expand All @@ -157,12 +157,12 @@ def _hit(self, advancing, **kwargs):
}
"""
del advancing
self.machine.events.post(self.name + '_hit')
self.machine.events.post(self.name + '_hit', shot=shot)
'''event: (name)_hit
desc: A member shots in the shot group called (name)
has been hit.
'''
self.machine.events.post("{}_{}_hit".format(self.name, kwargs['state']))
self.machine.events.post("{}_{}_hit".format(self.name, kwargs['state']), shot=shot)
'''event: (name)_(state)_hit
desc: A member shot with state (state) in the shot group (name)
has been hit.
Expand Down
15 changes: 13 additions & 2 deletions mpf/modes/attract/code/attract.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"""Contains the Attract class which is the attract mode in a pinball machine."""

from mpf.core.mode import Mode
from mpf.modes.carousel.code.carousel import Carousel


class Attract(Mode):
class Attract(Carousel):

"""Default mode running in a machine when a game is not in progress.
Expand All @@ -22,6 +22,13 @@ def __init__(self, *args, **kwargs):
self.start_hold_time = 0.0
self.start_buttons_held = list()

def mode_init(self):
"""Initialize the mode and handle missing carousel items."""
try:
super().mode_init()
except AssertionError:
pass

def mode_start(self, **kwargs):
"""Start the attract mode."""
# register switch handlers for the start button press so we can
Expand Down Expand Up @@ -50,6 +57,10 @@ def mode_start(self, **kwargs):
playfield.ball_search.enable()
playfield.ball_search.start()

# Check for carousel items
if self._all_items:
super().mode_start(**kwargs)

def start_button_pressed(self):
"""Handle start button press.
Expand Down
1 change: 1 addition & 0 deletions mpf/modes/high_score/code/high_score.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ async def _ask_player_for_initials(self, player: Player, award_label: str, value
input_initials = choice(unused_initials)
return input_initials

# pylint: disable-msg=too-many-arguments
async def _show_award_slide(self, player_num, player_name: str, category_name: str, award: str, value: int) -> None:
if not self.high_score_config['award_slide_display_time']:
return
Expand Down
71 changes: 40 additions & 31 deletions mpf/platforms/fast/fast_audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

from math import ceil
from mpf.core.logging import LogMixin
from mpf.core.settings_controller import SettingEntry

VARIABLE_NAME = "fast_audio_%s_volume"
SETTING_TYPE = "hw_volume"


class FASTAudioInterface(LogMixin):
Expand All @@ -17,9 +21,9 @@ def __init__(self, platform, communicator):
self.machine = platform.machine
self.communicator = communicator
self.amps = {
'main': {'name': 'fast_audio_main', 'label': "Speakers"},
'sub': {'name': 'fast_audio_sub', 'label': "Subwoofer"},
'headphones': {'name': 'fast_audio_headphones', 'label': "Headphones"}
'main': {'name': 'fast_audio_main', 'label': "Speakers", 'sort': 0},
'sub': {'name': 'fast_audio_sub', 'label': "Subwoofer", 'sort': 1},
'headphones': {'name': 'fast_audio_headphones', 'label': "Headphones", 'sort': 2}
}
self.control_pin_pulse_times = list()

Expand All @@ -35,30 +39,41 @@ def _initialize(self, **kwargs):
self._register_event_handlers()

def _configure_machine_vars(self):
for amp_name in self.amps:
var_name = f'fast_audio_{amp_name}_volume'

# Is there a race condition on first boot if the linked amp
# comes in the iteration before 'main' is written?
if amp_name != 'main' and self.communicator.config[f'link_{amp_name}_to_main']:
self.machine.variables.set_machine_var(
name=var_name,
value=self.machine.variables.get_machine_var('fast_audio_main_volume'),
persist=self.communicator.config['persist_volume_settings']
)
elif not self.machine.variables.is_machine_var(var_name):
self.machine.variables.set_machine_var(
name=var_name,
value=self.communicator.config[f'default_{amp_name}_volume'],
persist=self.communicator.config['persist_volume_settings']
)
# See if main volume has been defined yet, otherwise use default
main_volume = self.machine.variables.get_machine_var('fast_audio_main_volume')
if main_volume is None:
main_volume = self.communicator.config['default_main_volume']

for amp_name, settings in self.amps.items():

default_value = self.communicator.config[f'default_{amp_name}_volume']
if self.communicator.config.get(f'link_{amp_name}_to_main', False):
machine_var_name = VARIABLE_NAME % "main"
else:
machine_var_name = VARIABLE_NAME % amp_name

# Create a machine variable if one doesn't exist
if not self.machine.variables.is_machine_var(machine_var_name):
self.machine.variables.set_machine_var(machine_var_name, default_value,
self.communicator.config['persist_volume_settings'])

# Identify the machine var for this amp
settings["machine_var"] = machine_var_name
self.machine.settings.add_setting(SettingEntry(
settings['name'],
settings['label'],
settings['sort'],
machine_var_name,
default_value,
None,
SETTING_TYPE
))

def _init_amps(self):
for amp_name, amp in self.amps.items():
amp['steps'] = self.communicator.config[f'{amp_name}_steps']
amp['max_volume'] = self.communicator.config[f'max_hw_volume_{amp_name}']
amp['levels_list'] = self.communicator.config[f'{amp_name}_levels_list']
amp['link_to_main'] = self.communicator.config[f'link_{amp_name}_to_main']

# Just set everything here. The communicator will send the
# config as part of its init process later
Expand All @@ -71,7 +86,8 @@ def _init_amps(self):
# if we have a levels list in the config, make sure the steps num is right
amp['steps'] = len(amp['levels_list']) - 1

if amp['link_to_main'] and len(amp['levels_list']) != len(self.amps['main']['levels_list']):
if self.communicator.config[f'link_{amp_name}_to_main'] and \
len(amp['levels_list']) != len(self.amps['main']['levels_list']):
raise AssertionError(f"Cannot link {amp_name} to main. The number of volume steps must be the same. "
f"Main has {len(self.amps['main']['levels_list'])} steps, "
f"but {amp_name} has {len(amp['levels_list'])} steps.")
Expand Down Expand Up @@ -135,7 +151,6 @@ def send_volume_to_hw(self, amp_name=None, send_now=True):
for each_amp_name in self.amps:
self.send_volume_to_hw(each_amp_name, send_now)
return

self.communicator.set_volume(amp_name, self.get_volume(amp_name), send_now)

def _set_volume(self, amp_name, value=0, **kwargs):
Expand All @@ -155,19 +170,13 @@ def _set_volume(self, amp_name, value=0, **kwargs):
#self.platform.debug_log("Writing FAST amp volume %s to %s (decimal)", amp_name, value)
self.send_volume_to_hw(amp_name)

if amp_name == 'main':
for other_amp_name, other_amp in self.amps.items():
if other_amp_name != amp_name and other_amp['link_to_main']:
# Update the machine var, which will be caught and handled
self._set_machine_var_volume(other_amp_name, value)

def get_volume(self, amp_name, **kwargs):
"""Return the current volume of the specified amp."""
del kwargs
return self.machine.variables.get_machine_var(f'fast_audio_{amp_name}_volume')
return self.machine.variables.get_machine_var(self.amps[amp_name]["machine_var"]) or 0

def _set_machine_var_volume(self, amp_name, value):
self.machine.variables.set_machine_var(f'fast_audio_{amp_name}_volume', value)
self.machine.variables.set_machine_var(self.amps[amp_name]["machine_var"], value)

def temp_volume(self, amp_name, change=1, **kwargs):
"""Temporarily change the volume by the specified number of units, up or down.
Expand Down
8 changes: 8 additions & 0 deletions mpf/tests/machine_files/fast/config/audio.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ fast:
power_pulse_time: 98
reset_pulse_time: 97

machine_vars:
fast_audio_sub_volume:
initial_value: 9
fast_audio_main_volume:
initial_value: 8
fast_audio_headphones_volume:
initial_value: 10

variable_player:
increase_main_volume:
fast_audio_main_volume:
Expand Down
1 change: 1 addition & 0 deletions mpf/tests/machine_files/fast/config/audio2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fast:
- 14
- 15
headphones_level: line
link_sub_to_main: true

machine_vars:
fast_audio_main_volume:
Expand Down
23 changes: 14 additions & 9 deletions mpf/tests/test_Fast_Audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,15 @@ def create_expected_commands(self):
self.serial_connections['aud'].autorespond_commands['WD:3E8'] = 'WD:3E8,03'

else:
self.serial_connections['aud'].expected_commands = {'AM:0B':'AM:0B',
'AV:08':'AV:08',
'AS:09':'AS:09',
'AH:0A':'AH:0A',}
self.serial_connections['aud'].expected_commands = {
# 'AV:00':'AV:00',
# 'AS:00':'AS:00',
# 'AH:00':'AH:00',
'AM:0B':'AM:0B',
'AV:08':'AV:08',
'AS:09':'AS:09',
'AH:0A':'AH:0A',
}

def test_audio_basics(self):

Expand Down Expand Up @@ -69,10 +74,6 @@ def test_audio_basics(self):
self.assertTrue(fast_audio.communicator.amps['sub']['enabled'])
self.assertTrue(fast_audio.communicator.amps['headphones']['enabled'])

self.assertFalse(fast_audio.amps['main']['link_to_main'])
self.assertFalse(fast_audio.amps['sub']['link_to_main'])
self.assertFalse(fast_audio.amps['headphones']['link_to_main'])

# Change the volume var and make sure it's reflected in the hardware
self.aud_cpu.expected_commands['AV:0D'] = 'AV:0D'
self.machine.variables.set_machine_var('fast_audio_main_volume', 13)
Expand Down Expand Up @@ -113,9 +114,13 @@ def test_control_pins(self):
@test_config('audio2.yaml')
def test_machine_var_loading(self):
fast_audio = self.machine.default_platform.audio_interface
self.advance_time_and_run()
self.assertEqual(15, self.machine.variables.get_machine_var('fast_audio_main_volume'))
self.assertEqual(15, fast_audio.get_volume('main'))
self.assertEqual(15, fast_audio.communicator.amps['main']['volume'])
# sub has a machine value set in the configs, which is persisted
self.assertEqual(2, self.machine.variables.get_machine_var('fast_audio_sub_volume'))
# sub is linked to main, so it will be 15 even though the config value is 2
self.assertEqual(15, self.machine.variables.get_machine_var('fast_audio_sub_volume'))
self.assertEqual(15, fast_audio.get_volume('main'))
self.assertEqual(15, fast_audio.communicator.amps['sub']['volume'])
self.assertEqual(17, self.machine.variables.get_machine_var('fast_audio_headphones_volume'))

0 comments on commit 811bb24

Please sign in to comment.