Skip to content

Commit

Permalink
add light_sync method for batching
Browse files Browse the repository at this point in the history
  • Loading branch information
jabdoa2 committed Mar 11, 2017
1 parent 5f66577 commit 0efcd87
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 47 deletions.
7 changes: 7 additions & 0 deletions mpf/core/platform.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,13 @@ 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):
"""Called after channels of a light were updated.
Can be used if multiple channels need to be flushed at once.
"""
pass

@abc.abstractmethod
def configure_light(self, number, subtype, platform_settings) -> LightPlatformInterface:
"""Subclass this method in a platform module to configure a light.
Expand Down
40 changes: 7 additions & 33 deletions mpf/devices/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Light(SystemWideDevice):
def __init__(self, machine, name):
"""Initialise light."""
self.hw_drivers = {}
self.platforms = set()
self._color = [0, 0, 0]
self._corrected_color = [0, 0, 0]
super().__init__(machine, name)
Expand Down Expand Up @@ -155,15 +156,16 @@ def _load_hw_drivers(self):

for color, channel in channels.items():
channel = self.machine.config_validator.validate_config("light_channels", channel)
self.hw_drivers[color] = self._load_hw_driver(channel, color)
self.hw_drivers[color] = self._load_hw_driver(channel)

def _load_hw_driver(self, channel, color):
def _load_hw_driver(self, channel):
"""Load one channel."""
if channel['platform'] == "drivers":
return DriverLight(self.machine.coils[channel['number'].strip()], self.machine.clock.loop,
int(1 / self.machine.config['mpf']['default_light_hw_update_hz'] * 1000))
else:
platform = self.machine.get_platform_sections('lights', channel['platform'])
self.platforms.add(platform)
return platform.configure_light(channel['number'], channel['subtype'], channel['platform_settings'])

def _initialize(self):
Expand Down Expand Up @@ -327,6 +329,9 @@ def _schedule_update(self):
for color, hw_driver in self.hw_drivers.items():
hw_driver.set_fade(partial(self._get_brightness_and_fade, color=color))

for platform in self.platforms:
platform.light_sync()

def clear_stack(self):
"""Remove all entries from the stack and resets this light to 'off'."""
self.stack[:] = []
Expand Down Expand Up @@ -438,34 +443,3 @@ def get_color(self):
def fade_in_progress(self) -> bool:
"""Return true if a fade is in progress."""
return bool(self.stack and self.stack[0]['dest_time'] > self.machine.clock.get_time())

def write_color_to_hw_driver(self):
"""Set color to hardware platform.
Physically update the light hardware object based on the 'color'
setting of the highest priority setting from the stack.
This method is automatically called whenever a color change has been
made (including when fades are active).
"""
if self.fade_in_progress and self.stack and self.stack[0]['dest_time'] < self.machine.clock.get_time():
self.fade_in_progress = False

color = self.get_color()
corrected_color = self.gamma_correct(color)
corrected_color = self.color_correct(corrected_color)

self._color = list(color)
self._corrected_color = corrected_color
self.debug_log("Writing color to hw driver: %s", corrected_color)

for color, hw_driver in self.hw_drivers.items():
# TODO: implement fade_ms here
fade_ms = 0
if color in ["red", "blue", "green"]:
hw_driver.set_brightness(getattr(corrected_color, color) / 255.0, fade_ms)
elif color == "white":
hw_driver.set_brightness(
min(corrected_color.red, corrected_color.green, corrected_color.blue) / 255.0, fade_ms)
else:
raise AssertionError("Invalid color {} in light {}".format(color, self.name))
25 changes: 16 additions & 9 deletions mpf/platforms/opp/opp.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
"""
import logging
import asyncio
from typing import Dict
from typing import List

from mpf.platforms.opp.opp_neopixel import OPPNeopixel

from mpf.platforms.base_serial_communicator import BaseSerialCommunicator

Expand Down Expand Up @@ -48,15 +52,14 @@ def __init__(self, machine):
self.inpDict = dict()
self.inpAddrDict = dict()
self.read_input_msg = {}
self.opp_neopixels = []
self.opp_neopixels = [] # type: List[OPPNeopixelCard]
self.neoCardDict = dict()
self.neoDict = dict()
self.neoDict = dict() # type: Dict[str, OPPNeopixel]
self.numGen2Brd = 0
self.gen2AddrArr = {}
self.badCRC = 0
self.minVersion = 0xffffffff
self._poll_task = None
self._light_update_task = None

self.features['tickless'] = True

Expand Down Expand Up @@ -94,9 +97,6 @@ def stop(self):
if self._poll_task:
self._poll_task.cancel()

if self._light_update_task:
self._light_update_task.cancel()

for connections in self.serial_connections:
connections.stop()

Expand Down Expand Up @@ -608,13 +608,20 @@ def configure_light(self, number, subtype, platform_settings):
"light (incand board), with number %s "
"which doesn't exist", number)

if not self._light_update_task:
self._light_update_task = self.machine.clock.loop.create_task(self._update_lights())
self._light_update_task.add_done_callback(self._done)
return self.incandDict[number]
else:
raise AssertionError("Unknown subtype {}".format(subtype))

def light_sync(self):
"""Update lights."""
# first neo pixels
for light in self.neoDict.values():
if light.dirty:
light.update_color()

# then incandescents
self.update_incand()

@staticmethod
def _done(future): # pragma: no cover
"""Evaluate result of task.
Expand Down
12 changes: 7 additions & 5 deletions mpf/platforms/opp/opp_neopixel.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,18 +70,20 @@ def __init__(self, number, neo_card):
self.neoCard = neo_card
_, index = number.split('-')
self.index_char = chr(int(index))
self._color = [None, None, None]
self._color = [0, 0, 0]
self.dirty = False

self.log.debug("Creating OPP Neopixel: %s", number)

def set_channel(self, index, brightness):
"""Set one channel."""
self._color[index] = brightness
self.dirty = True

# this is to prevent intermediate color table entries. not ideal
if self._color[0] is not None and self._color[1] is not None and self._color[2] is not None:
self.color(self._color)
self._color = [None, None, None]
def update_color(self):
"""Update neopixel."""
self.color(self._color)
self.dirty = False

def color(self, color):
"""Instantly set this LED to the color passed.
Expand Down

0 comments on commit 0efcd87

Please sign in to comment.