Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FAST Retro Platform: System11 Support #1769

Merged
merged 27 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ee84fdd
FAST Platform command for virtual AC Relay switch
avanwinkle Aug 11, 2022
d357029
Merge branch 'dev' into fast-ac-relay-switch
avanwinkle Aug 11, 2022
139fd0a
Refactor System11Platform side_enabled property dynamic based on switch
avanwinkle Aug 12, 2022
dabd644
Aggressively clear C queue and prevent C actions when A side is needed
avanwinkle Aug 15, 2022
e83aef8
Merge branch 'dev' into fast-ac-relay-switch
avanwinkle Oct 16, 2022
867d6c0
Post event serial_error on serial error
avanwinkle Mar 25, 2023
d5caf4c
Allow start_step in show_tokens for shot profiles
avanwinkle May 17, 2023
cbbfe8a
Allow // for floor division in dynamic values
avanwinkle May 17, 2023
0f4dd30
Multiball lock ball handler fixes for multiple-eject devices
avanwinkle May 20, 2023
2f02858
Merge branch 'pt-sov-stable' of github.com:avanwinkle/mpf into pt-sov…
avanwinkle May 20, 2023
98adb83
Safety check empty eject queue before clearing double eject
avanwinkle May 21, 2023
b52afcc
BCP Interface support for specific sub-settings menu requests
avanwinkle Jul 20, 2023
40230ce
Include 'source' kwarg in bonus score award
avanwinkle Jul 20, 2023
11f7f3e
Convert data_manager from thread to asyncio for file writes
avanwinkle Jul 27, 2023
dd59424
Revert "Convert data_manager from thread to asyncio for file writes"
avanwinkle Jul 27, 2023
2235440
Bugfix settings machine vars being reset on new writes
avanwinkle Aug 29, 2023
f11254a
Merge pull request #1709 from avanwinkle/bugfix-settings-machine-vars…
avanwinkle Aug 31, 2023
ed3ad19
Merge remote-tracking branch 'refs/remotes/origin/pt-sov-stable' into…
avanwinkle Aug 31, 2023
c339206
Include explicit key_type in SettingsEntry
avanwinkle Aug 30, 2023
da2eef0
Revert "Include explicit key_type in SettingsEntry"
avanwinkle Sep 6, 2023
4e6884b
Merge branch 'dev' into pt-sov-dev
avanwinkle Feb 24, 2024
34c97d0
Add support for retro platform
avanwinkle Feb 28, 2024
85da366
Use FastDriverConfig for A/C Relay driver
avanwinkle Mar 1, 2024
26b9928
Fix autodetection and optional ports for FAST boards
avanwinkle Mar 1, 2024
9f5ab36
Update Sys11 AC relay switch commands
avanwinkle Mar 1, 2024
b67ad90
Ensure NET pause headers are minimum 3 chars
avanwinkle Mar 1, 2024
790b5b0
Log serial communicator exceptions before swallowing
avanwinkle Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions mpf/devices/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ def timed_enable(self, timed_enable_ms: int = None, hold_power: float = None, pu
# Let the PSU wait for both the pulse _and_ the timed enable
wait_ms = self._notify_psu_and_get_wait_ms(pulse_duration + hold_duration, max_wait_ms)

self.info_log("Pulsing Driver for %sms (%s pulse_power) with timed enable for %sms (%s hold_power)",
pulse_duration, pulse_power, hold_duration, hold_power)
# TODO: Detect a NotImplementedError and simulate a timed_enable
# with a software timer and enable+disable
self.hw_driver.timed_enable(PulseSettings(pulse_power, pulse_duration),
Expand Down
9 changes: 7 additions & 2 deletions mpf/platforms/fast/communicators/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __init__(self, platform, processor, config):
else:
self.watchdog_cmd = None

self.configure_logging(logger=f'[{self.remote_processor}]', console_level=config['debug'],
self.configure_logging(logger=f'FAST [{self.remote_processor}]', console_level=config['debug'],
file_level=config['debug'], url_base='https://fastpinball.com/mpf/error')
# TODO change these to not be hardcoded
# TODO do something with the URL endpoint
Expand Down Expand Up @@ -349,10 +349,14 @@ def _dispatch_incoming_msg(self, msg):
self.no_response_waiting.set()

# if the msg_header matches the first chars of the self.pause_sending_until, unpause sending
# Note that the msg_header includes the colon (e.g. "DL:", "SA:") and therefore
# pause_sending_until must also include the colon.
if self.pause_sending_flag.is_set() and self.pause_sending_until.startswith(msg_header):
self._resume_sending()

def pause_sending(self, msg_header):
if __debug__:
assert len(msg_header) >= 3, f"Confirmation headers should be at least three characters, received '{msg_header}'"
self.pause_sending_until = msg_header
self.pause_sending_flag.set()

Expand Down Expand Up @@ -409,7 +413,8 @@ async def _socket_writer(self):
if self.pause_sending_flag.is_set():
await self.pause_sending_flag.wait()

except:
except Exception as e:
self.log.error(e)
return # TODO better way to catch shutting down?

def write_to_port(self, msg, log_msg=None):
Expand Down
2 changes: 1 addition & 1 deletion mpf/platforms/fast/communicators/net_neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ async def reset_drivers(self):

# self.drivers contains a list of all drivers, not just ones defined in the config
for driver in self.drivers:
await self.send_and_wait_for_response_processed(f'{self.DRIVER_CMD}:{Util.int_to_hex_string(driver.number)}', self.DRIVER_CMD)
await self.send_and_wait_for_response_processed(f'{self.DRIVER_CMD}:{Util.int_to_hex_string(driver.number)}', f"{self.DRIVER_CMD}:")

self.platform.drivers_initialized = True

Expand Down
4 changes: 3 additions & 1 deletion mpf/platforms/fast/fast.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ def __init__(self, machine):
else:
self.raise_config_error(f'Unknown machine_type "{self.machine_type}" configured fast.', 6)

# Even though System11 uses ticks, that's handled on the Overlay and not needed here.
self.features['tickless'] = True
# Most FAST platforms don't use ticks, but System11 does
self.features['tickless'] = self.machine_type != 'sys11'
#self.features['tickless'] = self.machine_type != 'sys11'
self.features['max_pulse'] = 25500

self.serial_connections = dict()
Expand Down
1 change: 1 addition & 0 deletions mpf/platforms/fast/fast_defines.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
(11914, 4156): ('aud', 'FAST Audio Interface'),
(11914, 4157): ('exp', 'FAST Expansion Board'),
(11914, 4158): ('dsp', 'FAST Display Controller'),
(5824, 1163): ('net', 'FAST Retro Controller'), # Teensyduino
(1027, 24593): ('net', 'FAST Nano Controller'), # FTDI Quad RS232-HS
}

Expand Down
37 changes: 30 additions & 7 deletions mpf/platforms/fast/fast_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class FASTDriver:
"""Base class for drivers connected to a FAST Controller."""

__slots__ = ["log", "communicator", "number", "hw_number", "autofire_config", "baseline_driver_config",
"current_driver_config", "mode_param_mapping", "platform_settings"]
"config", "current_driver_config", "mode_param_mapping", "platform_settings"]

def __init__(self, communicator: FastSerialCommunicator, hw_number: int) -> None:
"""Initialize the driver object.
Expand All @@ -42,6 +42,7 @@ def __init__(self, communicator: FastSerialCommunicator, hw_number: int) -> None
self.communicator = communicator
self.number = hw_number # must be int to work with the rest of MPF
self.hw_number = Util.int_to_hex_string(hw_number) # hex version the FAST hw actually uses
self.config = None
self.autofire_config = None
self.platform_settings = dict()

Expand All @@ -56,6 +57,7 @@ def __init__(self, communicator: FastSerialCommunicator, hw_number: int) -> None
'12': ['pwm1_ms', 'pwm1_power', 'pwm2_ms', 'pwm2_power', 'kick_ms'],
'18': ['pwm1_ms', 'pwm1_power', 'pwm2_power', 'recycle_ms', None],
'20': ['off_switch', 'pwm1_ms', 'pwm1_power', 'pwm2_power', 'rest_ms'],
'25': ['relay_on_report_ms', 'relay_off_report_ms'],
'30': ['delay_ms_x10', 'pwm1_ms', 'pwm2_ms', 'pwm2_power', 'recycle_ms'],
'70': ['pwm1_ms', 'pwm1_power', 'pwm2_ms_x100', 'pwm2_power', 'recycle_ms'],
'75': ['off_switch', 'pwm1_ms', 'pwm2_ms_x100', 'pwm2_power', 'recycle_ms'],
Expand All @@ -73,7 +75,7 @@ def set_initial_config(self, mpf_config: DriverConfig, platform_settings):

This will not be called for drivers that are not in the MPF config.
"""

self.config = mpf_config
self.platform_settings = platform_settings
self.current_driver_config = self.convert_mpf_config_to_fast(mpf_config, platform_settings)
self.baseline_driver_config = copy(self.current_driver_config)
Expand Down Expand Up @@ -144,8 +146,8 @@ def clear_bit(self, hex_string, bit):
return Util.int_to_hex_string(num)

def send_config_to_driver(self, one_shot: bool = False, wait_to_confirm: bool = False):
self.log.debug("Sending config to driver %s. one_shot: %s. wait_to_confirm: %s",
self.number, one_shot, wait_to_confirm)
self.log.debug("Sending config to driver %s (0x%s). one_shot: %s. wait_to_confirm: %s",
self.number, self.hw_number, one_shot, wait_to_confirm)

if one_shot:
trigger = self.set_bit(self.current_driver_config.trigger, 3)
Expand All @@ -157,7 +159,7 @@ def send_config_to_driver(self, one_shot: bool = False, wait_to_confirm: bool =
f'{self.current_driver_config.param2},{self.current_driver_config.param3},{self.current_driver_config.param4},'
f'{self.current_driver_config.param5}')
if wait_to_confirm:
self.communicator.send_with_confirmation(msg, f'{self.communicator.DRIVER_CMD}')
self.communicator.send_with_confirmation(msg, f'{self.communicator.DRIVER_CMD}:')
else:
self.communicator.send_and_forget(msg)

Expand Down Expand Up @@ -354,17 +356,38 @@ def clear_autofire(self):
self.autofire_config = None
self.communicator.send_and_forget(f'{self.communicator.TRIGGER_CMD}:{self.hw_number},02')

def set_relay(self, relay_switch, debounce_closed_ms, debounce_open_ms):
"""Set an AC Relay rule with virtual switch."""

self.log.debug("Setting A/C Relay for driver %s (0x%s) and switch %s (0x%s)",
self.number, self.hw_number, relay_switch.number, relay_switch.hw_number)
self.current_driver_config = FastDriverConfig(number=self.hw_number, trigger='81',
switch_id=relay_switch.hw_number,
mode='25',
param1=Util.int_to_hex_string(debounce_closed_ms),
param2=Util.int_to_hex_string(debounce_open_ms),
param3='00',
param4='00',
param5='00')
self.send_config_to_driver(wait_to_confirm=True)

def enable(self, pulse_settings: PulseSettings, hold_settings: HoldSettings):
"""Enable (turn on) this driver."""

self.log.debug("Enabling (turning on) driver %s with pulse_settings: %s and hold_settings: %s.",
self.number, pulse_settings, hold_settings)
self.log.debug("Enabling (turning on) driver %s (0x%s) mode %s with pulse_settings: %s and hold_settings: %s.",
self.number, self.hw_number, self.current_driver_config.mode, pulse_settings, hold_settings)

self._check_and_clear_delay()

reconfigured = False
mode = self.current_driver_config.mode

# AC Relays have special behavior
if mode == '25':
self.log.debug(" - A/C Relay activating!")
self.communicator.send_and_forget(f'{self.communicator.TRIGGER_CMD}:{self.hw_number},03')
return

pwm1_ms = Util.int_to_hex_string(pulse_settings.duration)
pwm1_power = Util.float_to_pwm8_hex_string(pulse_settings.power)
pwm2_power = Util.float_to_pwm8_hex_string(hold_settings.power)
Expand Down
11 changes: 5 additions & 6 deletions mpf/platforms/fast/fast_port_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,14 @@ async def _connect_task(self, port, baud):

total_attempts = 5
attempts = 0
while True:
while attempts < total_attempts:
writer.write(b'ID:\r')

# Wait for a response with 1-second timeout
try:
data = await asyncio.wait_for(reader.read(100), timeout=1.0)
except asyncio.TimeoutError:
attempts += 1
if attempts < total_attempts:
continue # retry
else:
self.platform.debug_log("Unable to get ID: on port %s after %s retries.", port, total_attempts)
pass

if data:
data = data.decode('utf-8', errors='ignore')
Expand All @@ -91,6 +87,9 @@ async def _connect_task(self, port, baud):
self._report_success(processor, port)
writer.close()
return
attempts += 1
self.platform.debug_log("Unable to get ID: on port %s after %s retries.", port, total_attempts)
return

def _report_success(self, processor, port):
self.results[processor] = port
Expand Down
Loading
Loading