text: """Contains the parent class for all platforms."""
  import abc
  import asyncio
  from collections import namedtuple
  
  from typing import Optional, Dict, List
  
  from mpf.core.logging import LogMixin
  from mpf.core.utility_functions import Util
  
  MYPY = False
  if MYPY:   # pragma: no cover
      from mpf.devices.switch import Switch   # pylint: disable-msg=cyclic-import,unused-import
      from mpf.devices.stepper import Stepper     # pylint: disable-msg=cyclic-import,unused-import
      from mpf.platforms.interfaces.driver_platform_interface import DriverPlatformInterface  # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.switch_platform_interface import SwitchPlatformInterface  # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.light_platform_interface import LightPlatformInterface    # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.servo_platform_interface import ServoPlatformInterface    # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.segment_display_platform_interface import SegmentDisplayPlatformInterface     # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.hardware_sound_platform_interface import HardwareSoundPlatformInterface   # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.stepper_platform_interface import StepperPlatformInterface    # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.accelerometer_platform_interface import AccelerometerPlatformInterface    # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.platforms.interfaces.i2c_platform_interface import I2cPlatformInterface    # pylint: disable-msg=cyclic-import,unused-import; # noqa
      from mpf.core.machine import MachineController  # pylint: disable-msg=cyclic-import,unused-import; # noqa
  
  
  class BasePlatform(LogMixin, metaclass=abc.ABCMeta):
  
      """Base class for all hardware platforms in MPF."""
  
      __slots__ = ["machine", "features", "debug"]
  
      def __init__(self, machine):
          """Create features and set default variables.
  
          Args:
              machine(mpf.core.machine.MachineController): The machine.
          """
          self.machine = machine  # type: MachineController
          self.features = {}
          super().__init__()
          self.debug = False
  
          # Set default platform features. Each platform interface can change
          # these to notify the framework of the specific features it supports.
          self.features['has_dmds'] = False
          self.features['has_rgb_dmds'] = False
          self.features['has_accelerometers'] = False
          self.features['has_i2c'] = False
          self.features['has_servos'] = False
          self.features['has_lights'] = False
          self.features['has_switches'] = False
          self.features['has_drivers'] = False
          self.features['tickless'] = False
          self.features['has_segment_displays'] = False
          self.features['has_hardware_sound_systems'] = False
          self.features['has_steppers'] = False
          self.features['allow_empty_numbers'] = False
  
      def assert_has_feature(self, feature_name):
          """Assert that this platform has a certain feature or raise an exception otherwise."""
          if not self.features.get("has_{}".format(feature_name), False):
              self.raise_config_error("Platform {} does not support to configure {feature_name}. "
                                      "Please make sure the platform "
                                      "you configured for {feature_name} actually supports that type "
                                      "of devices.".format(self.__class__, feature_name=feature_name), 99)
  
      def _configure_device_logging_and_debug(self, logger_name, config):
          """Configure logging for platform."""
          if config['debug']:
              self.debug = True
              config['console_log'] = 'full'
              config['file_log'] = 'full'
  
          self.configure_logging(logger_name,
                                 config['console_log'],
                                 config['file_log'])
  
      @classmethod
      def get_config_spec(cls):
          """Return config spec for this platform."""
          return False
  
      # pylint: disable-msg=no-self-use
      def get_info_string(self) -> str:
          """Return information string about this platform."""
          return "Not implemented"
  
      # pylint: disable-msg=no-self-use
      def update_firmware(self) -> str:
          """Perform a firmware update."""
  
      async def initialize(self):
          """Initialise the platform.
  
          This is called after all platforms have been created and core modules have been loaded.
          """
  
      async def start(self):
          """Start receiving switch changes from this platform."""
  
      def tick(self):
          """Run task.
  
          Called periodically.
  
          Subclass this method in a platform module to perform periodic updates
          to the platform hardware, e.g. reading switches, sending driver or
          light updates, etc.
  
          """
  
      def stop(self):
          """Stop the platform.
  
          Subclass this method in the platform module if you need to perform
          any actions to gracefully stop the platform interface.
  
          This could do things like reseting it, stopping events, etc.
  
          This method will be called when MPF stops, including when an MPF thread
          crashes.
  
          """
  
  
  class DmdPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for DMDs in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add dmd feature."""
          super().__init__(machine)
          self.features['has_dmds'] = True
  
      @abc.abstractmethod
      def configure_dmd(self):
          """Subclass this method in a platform module to configure the DMD.
  
          This method should return a reference to the DMD's platform interface
          method will will receive the frame data.
  
          """
          raise NotImplementedError
  
  
  class HardwareSoundPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for hardware sounds in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add hardware sound feature."""
          super().__init__(machine)
          self.features['has_hardware_sound_systems'] = True
  
      @abc.abstractmethod
      def configure_hardware_sound_system(self) -> "HardwareSoundPlatformInterface":
          """Return a reference to the hardware sound interface."""
          raise NotImplementedError
  
  
  class RgbDmdPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for RGB DMDs in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add rgb dmd feature."""
          super().__init__(machine)
          self.features['has_rgb_dmds'] = True
  
      @abc.abstractmethod
      def configure_rgb_dmd(self, name: str):
          """Subclass this method in a platform module to configure the DMD.
  
          This method should return a reference to the DMD's platform interface
          method will will receive the frame data.
  
          """
          raise NotImplementedError
  
  
  class SegmentDisplayPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for 7-segment/6-digits display in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add segment display feature."""
          super().__init__(machine)
          self.features['has_segment_displays'] = True
  
      @abc.abstractmethod
      async def configure_segment_display(self, number: str, platform_settings) -> "SegmentDisplayPlatformInterface":
          """Subclass this method in a platform module to configure a segment display.
  
          This method should return a reference to the segment display platform interface
          method will will receive the text to show.
          """
          raise NotImplementedError
  
  
  # pylint: disable-msg=abstract-method
  class SegmentDisplaySoftwareFlashPlatform(SegmentDisplayPlatform, metaclass=abc.ABCMeta):
  
      """SegmentDisplayPlatform with software flash support."""
  
      __slots__ = ["_displays", "_display_flash_task"]
  
      def __init__(self, machine):
          """Initialise software flash support."""
          super().__init__(machine)
          self._displays = set()
          self._display_flash_task = None
  
      async def initialize(self):
          """Start flash task."""
          await super().initialize()
          self._display_flash_task = self.machine.clock.loop.create_task(self._display_flash())
          self._display_flash_task.add_done_callback(Util.raise_exceptions)
  
      async def _display_flash(self):
          wait_time = 1 / (self.config['display_flash_frequency'] * 2)
          while True:
              # set on
              await asyncio.sleep(wait_time, loop=self.machine.clock.loop)
              for display in self._displays:
                  display.set_software_flash(True)
              # set off
              await asyncio.sleep(wait_time, loop=self.machine.clock.loop)
              for display in self._displays:
                  display.set_software_flash(False)
  
      def stop(self):
          """Cancel flash task."""
          super().stop()
          if self._display_flash_task:
              self._display_flash_task.cancel()
  
      def _handle_software_flash(self, display):
          """Register display for flash task."""
          self._displays.add(display)
  
  
  class AccelerometerPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for Accelerometer platforms."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add accelerometer feature."""
          super().__init__(machine)
          self.features['has_accelerometers'] = True
  
      @abc.abstractmethod
      def configure_accelerometer(self, number: str, config: dict, callback) -> "AccelerometerPlatformInterface":
          """Configure accelerometer.
  
          Args:
              number: Number of this accelerometer
              config (dict): Configuration of this accelerometer
              callback (mpf.devices.accelerometer.Accelerometer): Callback device to send data to
          """
          raise NotImplementedError
  
  
  class I2cPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for I2C Platforms."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Initialise I2C platform and set feature."""
          super().__init__(machine)
          self.features['has_i2c'] = True
  
      async def configure_i2c(self, number: str) -> "I2cPlatformInterface":
          """Configure i2c device."""
          raise NotImplementedError
  
  
  class ServoPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for servo platforms in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add servo feature."""
          super().__init__(machine)
          self.features['has_servos'] = True
  
      @abc.abstractmethod
      async def configure_servo(self, number: str) -> "ServoPlatformInterface":
          """Configure a servo device in platform.
  
          Args:
              number: Number of the servo
          """
          raise NotImplementedError
  
  
  class StepperPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for smart servo (axis) platforms in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add smart servo feature."""
          super().__init__(machine)
          self.features['has_steppers'] = True
  
      @classmethod
      def get_stepper_config_section(cls) -> Optional[str]:
          """Return config section for additional stepper config items."""
          return None
  
      def validate_stepper_section(self, stepper: "Stepper", config: dict) -> dict:
          """Validate a stepper config for platform.
  
          Args:
              stepper: Stepper to validate.
              config: Config to validate.
  
          Returns: Validated config.
          """
          if self.get_stepper_config_section():
              spec = self.get_stepper_config_section()    # pylint: disable-msg=assignment-from-none
              config = stepper.machine.config_validator.validate_config(spec, config, stepper.name)
          elif config:
              raise AssertionError("No platform_config supported but not empty {} for stepper {}".
                                   format(config, stepper.name))
  
          return config
  
      @abc.abstractmethod
      async def configure_stepper(self, number: str, config: dict) -> "StepperPlatformInterface":
          """Configure a smart stepper (axis) device in platform.
  
          Args:
              number: Number of the smart servo
              config: Config for this stepper.
          """
          raise NotImplementedError
  
  
  class LightsPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for platforms with any kind of lights in MPF.
  
      This includes LEDs, GIs, Matrix Lights and any other lights.
      """
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add led feature."""
          super().__init__(machine)
          self.features['has_lights'] = True
  
      @abc.abstractmethod
      def parse_light_number_to_channels(self, number: str, subtype: str):
          """Parse light number to a list of channels."""
          raise NotImplementedError
  
      def light_sync(self):
          """Update lights synchonously.
  
          Called after channels of a light were updated. Can be used if multiple channels need to be flushed at once.
          """
  
      @abc.abstractmethod
      def configure_light(self, number: str, subtype: str, platform_settings: dict) -> "LightPlatformInterface":
          """Subclass this method in a platform module to configure a light.
  
          This method should return a reference to the light
          object which will be called to access the hardware.
          """
          raise NotImplementedError
  
  
  SwitchConfig = namedtuple("SwitchConfig", ["invert", "debounce"])
  
  
  class SwitchPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for platforms with switches in MPF."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add switch feature."""
          super().__init__(machine)
          self.features['has_switches'] = True
  
      @abc.abstractmethod
      def configure_switch(self, number: str, config: SwitchConfig, platform_config: dict) -> "SwitchPlatformInterface":
          """Subclass this method in a platform module to configure a switch.
  
          This method should return a reference to the switch's platform interface
          object which will be called to access the hardware.
  
          Args:
              number: Switch number.
              config : Config of switch.
              platform_config: Platform specific settings.
  
          """
          raise NotImplementedError
  
      @classmethod
      def get_switch_config_section(cls) -> Optional[str]:
          """Return config section for additional switch config items."""
          return None
  
      def validate_switch_section(self, switch: "Switch", config: dict) -> dict:
          """Validate a switch config for platform.
  
          Args:
              switch: Switch to validate.
              config: Config to validate.
  
          Returns: Validated config.
          """
          if self.get_switch_config_section():
              spec = self.get_switch_config_section()     # pylint: disable-msg=assignment-from-none
              config = switch.machine.config_validator.validate_config(spec, config, switch.name)
          elif config:
              raise AssertionError("No platform_config supported but not empty {} for switch {}".
                                   format(config, switch.name))
  
          return config
  
      @abc.abstractmethod
      async def get_hw_switch_states(self) -> Dict[str, bool]:
          """Get all hardware switch states.
  
          Subclass this method in a platform module to return the hardware
          states of all the switches on that platform.
          of a switch.
  
          This method should return a dict with the switch numbers as keys and the
          hardware state of the switches as values. (0 = inactive, 1 = active)
          This method should not compensate for NO or NC status, rather, it
          should return the raw hardware states of the switches.
  
          """
          raise NotImplementedError
  
  
  SwitchSettings = namedtuple("SwitchSettings", ["hw_switch", "invert", "debounce"])
  DriverSettings = namedtuple("DriverSettings", ["hw_driver", "pulse_settings", "hold_settings", "recycle"])
  DriverConfig = namedtuple("DriverConfig", ["default_pulse_ms", "default_pulse_power", "default_hold_power",
                                             "default_recycle", "max_pulse_ms", "max_pulse_power", "max_hold_power"])
  RepulseSettings = namedtuple("RepulseSettings", ["enable_repulse"])
  
  
  class DriverPlatform(BasePlatform, metaclass=abc.ABCMeta):
  
      """Baseclass for platforms with drivers."""
  
      __slots__ = []  # type: List[str]
  
      def __init__(self, machine):
          """Add driver feature and default max_pulse length."""
          super().__init__(machine)
  
          # Set default platform features. Each platform interface can change
          # these to notify the framework of the specific features it supports.
          self.features['has_drivers'] = True
          self.features['max_pulse'] = 255
  
      @abc.abstractmethod
      def configure_driver(self, config: DriverConfig, number: str, platform_settings: dict) -> "DriverPlatformInterface":
          """Subclass this method in a platform module to configure a driver.
  
          This method should return a reference to the driver's platform interface
          object which will be called to access the hardware.
  
          """
          raise NotImplementedError
  
      @abc.abstractmethod
      def clear_hw_rule(self, switch: SwitchSettings, coil: DriverSettings):
          """Subclass this method in a platform module to clear a hardware switch rule for this switch.
  
          Clearing a hardware rule means actions on this switch will no longer
          affect coils.
  
          Another way to think of this is that it 'disables' a hardware rule.
          This is what you'd use to disable flippers and autofire_coils during
          tilt, game over, etc.
  
          """
          raise NotImplementedError
  
      @classmethod
      def get_coil_config_section(cls) -> Optional[str]:
          """Return addition config section for coils."""
          return None
  
      def validate_coil_section(self, driver, config) -> dict:
          """Validate coil config for platform."""
          if self.get_coil_config_section():
              spec = self.get_coil_config_section()   # pylint: disable-msg=assignment-from-none
              config = driver.machine.config_validator.validate_config(spec, config, driver.name)
          elif config:
              raise AssertionError("No platform_config supported but not empty {} for driver {}".
                                   format(config, driver.name))
  
          return config
  
      @abc.abstractmethod
      def set_pulse_on_hit_rule(self, enable_switch: SwitchSettings, coil: DriverSettings):
          """Set pulse on hit rule on driver.
  
          Pulses a driver when a switch is hit. When the switch is released the pulse continues. Typically used for
          autofire coils such as pop bumpers.
          """
          raise NotImplementedError
  
      # pylint: disable-msg=no-self-use
      def set_delayed_pulse_on_hit_rule(self, enable_switch: SwitchSettings, coil: DriverSettings, delay_ms: int):
          """Set pulse on hit and release rule to driver.
  
          When a switch is hit and a certain delay passed it pulses a driver.
          When the switch is released the pulse continues.
          Typically used for kickbacks.
          """
          del enable_switch
          del coil
          del delay_ms
          raise AssertionError("This platform does not support delayed pulse hardware rules.")
  
      @abc.abstractmethod
      def set_pulse_on_hit_and_release_rule(self, enable_switch: SwitchSettings, coil: DriverSettings):
          """Set pulse on hit and release rule to driver.
  
          Pulses a driver when a switch is hit. When the switch is released the pulse is canceled. Typically used on
          the main coil for dual coil flippers without eos switch.
          """
          raise NotImplementedError
  
      @abc.abstractmethod
      def set_pulse_on_hit_and_enable_and_release_rule(self, enable_switch: SwitchSettings, coil: DriverSettings):
          """Set pulse on hit and enable and release rule on driver.
  
          Pulses a driver when a switch is hit. Then enables the driver (may be with pwm). When the switch is released
          the pulse is canceled and the driver gets disabled. Typically used for single coil flippers.
          """
          raise NotImplementedError
  
      @abc.abstractmethod
      def set_pulse_on_hit_and_release_and_disable_rule(self, enable_switch: SwitchSettings,
                                                        eos_switch: SwitchSettings, coil: DriverSettings,
                                                        repulse_settings: Optional[RepulseSettings]):
          """Set pulse on hit and enable and release and disable rule on driver.
  
          Pulses a driver when a switch is hit. When the switch is released
          the pulse is canceled and the driver gets disabled. When the eos_switch is hit the pulse is canceled
          and the driver becomes disabled. Typically used on the main coil for dual-wound coil flippers with eos switch.
          """
          raise NotImplementedError
  
      @abc.abstractmethod
      def set_pulse_on_hit_and_enable_and_release_and_disable_rule(self, enable_switch: SwitchSettings,
                                                                   eos_switch: SwitchSettings, coil: DriverSettings,
                                                                   repulse_settings: Optional[RepulseSettings]):
          """Set pulse on hit and enable and release and disable rule on driver.
  
          Pulses a driver when a switch is hit. Then enables the driver (may be with pwm). When the switch is released
          the pulse is canceled and the driver becomes disabled. When the eos_switch is hit the pulse is canceled
          and the driver becomes enabled (likely with PWM).
          Typically used on the coil for single-wound coil flippers with eos switch.
          """
          raise NotImplementedError
  