Exemple #1
0
 def animate_horizontal(self):
     tally_types = [t for t in TallyType if t != TallyType.all_tally]
     while tally_types[0] != self.cur_group:
         t = tally_types.pop(0)
         tally_types.append(t)
     for i, t in enumerate(tally_types):
         if t == TallyType.no_tally:
             continue
         tg = self.tally_groups[t]
         tg.reset_all()
         try:
             color = TallyColor(i+1)
         except ValueError:
             color = TallyColor.OFF
         tg.tally_colors[self.cur_index] = color
     try:
         if self.cur_group.value == 0:
             v = 1
         else:
             v = self.cur_group.value * 2
             if v > TallyType.all_tally.value:
                 raise ValueError()
         t = TallyType(v)
         self.cur_group = t
     except ValueError:
         self.cur_index += 1
         self.cur_group = TallyType.no_tally
         if self.cur_index >= self.num_tallies:
             self.set_animate_mode(AnimateMode.vertical)
Exemple #2
0
def unmatched_sconfs(unmatched_ttypes):
    return [[
        SingleTallyConfig(screen_index=0,
                          tally_index=0,
                          tally_type=TallyType.from_str(ttype))
        for ttype in ttypes
    ] for ttypes in unmatched_ttypes]
Exemple #3
0
 def __getitem__(self, key: StrOrTallyType) -> TallyColor:
     if not isinstance(key, TallyType):
         key = TallyType.from_str(key)
     if key.is_iterable:
         color = TallyColor.OFF
         for tt in key:
             color |= getattr(self, tt.name)
         return color
     return getattr(self, key.name)
Exemple #4
0
def test_single_tally_matching():
    tally_type = TallyType.rh_tally
    for i in range(10):
        all_screens = SingleTallyConfig(
            screen_index=None,
            tally_index=i,
            tally_type=tally_type,
        )
        assert all_screens.matches_screen(0xffff)

        for j in range(10):
            assert all_screens.matches_screen(j)

            tconf0 = SingleTallyConfig(
                screen_index=j,
                tally_index=i,
                tally_type=tally_type,
            )
            tconf1 = SingleTallyConfig(
                screen_index=j,
                tally_index=i + 1,
                tally_type=tally_type,
            )
            tconf2 = SingleTallyConfig(
                screen_index=j + 1,
                tally_index=i,
                tally_type=tally_type,
            )
            tconfs = [tconf0, tconf1, tconf2]

            for tconf in tconfs:
                assert tconf.matches(tconf.id, tconf.tally_type)
                assert tconf.matches(
                    tconf.id, tconf.tally_type, return_matched=True) is tconf
                assert tconf.matches(tconf,
                                     tally_type=TallyType.from_str('rh|lh'))
                assert not tconf.matches(tconf, tally_type=TallyType.txt_tally)
                assert tconf.matches_screen(0xffff)
                assert tconf.matches_screen(all_screens)
                assert all_screens.matches_screen(tconf)
                if tconf.tally_index == i:
                    assert tconf.matches(all_screens)
                    assert tconf.matches(all_screens.id,
                                         all_screens.tally_type)
                else:
                    assert not tconf.matches(all_screens)
                    assert not tconf.matches(all_screens.id,
                                             all_screens.tally_type)

            assert tconf0.matches_screen(tconf1)
            assert tconf0.matches_screen(j)
            assert not tconf0.matches_screen(tconf2)
            assert not tconf0.matches_screen(j + 1)
            assert not tconf0.matches(tconf1)
            assert not tconf0.matches(tconf1.id, tconf1.tally_type)
            assert not tconf0.matches(tconf2)
            assert not tconf0.matches(tconf2.id, tconf2.tally_type)
Exemple #5
0
 def __setitem__(self, key: StrOrTallyType, value: StrOrTallyColor):
     if not isinstance(key, TallyType):
         key = TallyType.from_str(key)
     if not isinstance(value, TallyColor):
         value = TallyColor.from_str(value)
     if key.is_iterable:
         for tt in key:
             setattr(self, tt.name, value)
     else:
         setattr(self, key.name, value)
Exemple #6
0
    def __init__(self, clients=None, num_tallies=8, update_interval=.5, screen=1, all_off_on_close=False):
        self.num_tallies = num_tallies
        self.update_interval = update_interval
        super().__init__(clients, all_off_on_close)
        self.screen = self.get_or_create_screen(screen)
        for i in range(self.num_tallies):
            self.screen.add_tally(i, text=string.ascii_uppercase[i])

        self.tally_groups = {}
        for tally_type in TallyType.all():
            tg = TallyTypeGroup(tally_type, self.num_tallies)
            self.tally_groups[tally_type] = tg
Exemple #7
0
 async def on_receiver_tally_change(self, tally: Tally,
                                    props_changed: Set[str], **kwargs):
     changed = set()
     for prop in props_changed:
         if prop not in TallyType.__members__:
             continue
         ttype = TallyType.from_str(prop)
         pixel = self.tally_type_map.get(tally.id + (ttype, ))
         color = self.get_merged_tally(tally, ttype)
         if color == self.get(pixel):
             continue
         self[pixel] = color
         changed.add(pixel)
     await self.queue_update(*changed)
Exemple #8
0
 def get_init_options(cls) -> Tuple[Option]:
     tt_choices = tuple((tt.name for tt in TallyType))
     return (
         Option(name='tally_index', type=int, required=True, title='Index'),
         Option(
             name='tally_type', type=str, required=True, choices=tt_choices,
             serialize_cb=lambda x: x.to_str(),
             validate_cb=lambda x: TallyType.from_str(x),
             title='TallyType',
         ),
         TallyColorOption,
         Option(name='screen_index', type=int, required=False, title='Screen'),
         Option(name='name', type=str, required=False, default='', title='Name'),
     )
Exemple #9
0
 async def wait_for_rx(tally_type):
     tally_types = set()
     if not isinstance(tally_type, TallyType):
         tally_type = TallyType.from_str(tally_type)
     if tally_type.is_iterable:
         for tt in tally_type:
             tally_types.add(tt.name)
     else:
         tally_types.add(tally_type.name)
     props = set()
     for _ in range(len(tally_types)):
         evt_args, evt_kwargs = await tally_listener.get()
         props |= evt_args[1]
         if props == tally_types:
             break
     assert props == tally_types
Exemple #10
0
 def build_multi_config(self) -> MultiTallyConfig:
     self.tally_type_map.clear()
     tconfs = []
     scr = self.config.screen_index
     start_index = self.config.tally_index
     for i in range(5):
         tally_index = start_index + i
         for j, ttype in enumerate(TallyType.all()):
             pixel = (j, i)
             tconf = SingleTallyConfig(screen_index=scr,
                                       tally_index=tally_index,
                                       tally_type=ttype)
             tconfs.append(tconf)
             key = tconf.tally_key + (ttype, )
             self.tally_type_map[key] = pixel
     self.multi_config = MultiTallyConfig(tallies=tconfs)
Exemple #11
0
 def decode(self, d):
     if '__class__' in d:
         cls = self.str_to_cls(d['__class__'])
         if cls is not None:
             if cls is DeviceMapping:
                 for key in ['program', 'preview']:
                     if not isinstance(d[key], TallyMap):
                         d[key] = self.decode(d[key])
             elif cls is TallyMap:
                 if not isinstance(d['tally_type'], TallyType):
                     if isinstance(d['tally_type'], int):
                         d['tally_type'] = TallyType(d['tally_type'])
                     else:
                         d['tally_type'] = self.decode(d['tally_type'])
             elif cls is TallyType:
                 return getattr(TallyType, d['name'])
             del d['__class__']
             return cls(**d)
     return d
Exemple #12
0
async def test_all_off_on_close(faker, udp_port):
    loop = asyncio.get_event_loop()

    sender = UmdSender(
        clients=[('127.0.0.1', udp_port)],
        all_off_on_close=True,
    )
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    add_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=add_listener.callback)

    tally_listener = EventListener()
    receiver.bind_async(loop, on_tally_updated=tally_listener.callback)

    async with receiver:
        async with sender:
            for screen_index in range(10):
                for i in range(10):
                    t_id = (screen_index, i)
                    sender.set_tally_text(t_id, f'Tally-{i}')
                    tx_tally = sender.tallies[t_id]

                    evt_args, evt_kwargs = await add_listener.get()
                    rx_tally = evt_args[0]
                    assert rx_tally == tx_tally

                    for ttype in TallyType.all():
                        setattr(tx_tally, ttype.name, TallyColor.RED)

                        evt_args, evt_kwargs = await tally_listener.get()
                        assert getattr(rx_tally, ttype.name) == TallyColor.RED

        # Sender is closed and should have broadcast "all-off"
        _ = await asyncio.wait_for(tally_listener.get(), timeout=1)
        while not tally_listener.empty():
            _ = await tally_listener.get()
        for rx_tally in receiver.tallies.values():
            assert rx_tally.rh_tally == TallyColor.OFF
            assert rx_tally.txt_tally == TallyColor.OFF
            assert rx_tally.lh_tally == TallyColor.OFF
Exemple #13
0
def test_memoization():
    mconf0 = MultiTallyConfig(screen_index=None, allow_all=True)
    mconf1 = MultiTallyConfig()

    for i in range(8):
        for j in range(16):
            for tally_type in TallyType.all():
                conf = SingleTallyConfig(
                    screen_index=i,
                    tally_index=j,
                    tally_type=tally_type,
                )
                mconf1.tallies.append(conf)

    for i in range(8):
        screen = None
        for j in range(16):
            t_id = (i, j)

            tally_type = TallyType.rh_tally
            sconf0 = SingleTallyConfig(
                screen_index=i,
                tally_index=j,
                tally_type=tally_type,
            )
            match = mconf0.matches(sconf0, return_matched=True)
            assert match is sconf0
            match = mconf0.matches(sconf0, return_matched=True)
            assert match is sconf0
            match = mconf0.matches(sconf0.id, tally_type, return_matched=True)
            assert match is sconf0

            tally_type = TallyType.txt_tally
            sconf1 = mconf0.matches(t_id, tally_type, return_matched=True)
            assert sconf1.id == t_id
            assert sconf1.tally_type == tally_type
            assert sconf1 is not sconf0
            match = mconf0.matches(sconf1, return_matched=True)
            assert match is sconf1
            match = mconf0.matches(sconf1.id, tally_type, return_matched=True)
            assert match is sconf1
            match = mconf0.matches(sconf1, tally_type, return_matched=True)
            assert match is sconf1

            tally_type = TallyType.lh_tally
            tmp_conf = SingleTallyConfig(
                screen_index=i,
                tally_index=j,
                tally_type=tally_type,
            )
            screen, tally = tmp_conf.create_tally(screen)
            assert tally.id == t_id
            sconf2 = mconf0.matches(tally, tally_type, return_matched=True)
            assert sconf2.id == t_id
            assert sconf2.tally_type == tally_type

            for sconf in [sconf0, sconf1, sconf2]:
                match = mconf1.matches(sconf, return_matched=True)
                assert match == sconf
                assert match is not sconf
                assert match in mconf1.tallies
                assert mconf1.search_memoized(sconf) is match
                assert mconf1.search_memoized(sconf.id,
                                              sconf.tally_type) is match
            match = mconf1.search_memoized(tally, sconf2.tally_type)
            assert match == sconf2

    assert mconf0.memoized_tally_confs == mconf1.memoized_tally_confs
Exemple #14
0
async def test_with_uhs_data(uhs500_msg_bytes, uhs500_msg_parsed, udp_endpoint,
                             udp_port):
    transport, protocol, endpoint_port = udp_endpoint
    assert udp_port != endpoint_port

    loop = asyncio.get_event_loop()

    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    uhs_screen = uhs500_msg_parsed.screen

    async with receiver:

        # Send message bytes to receiver
        transport.sendto(uhs500_msg_bytes, ('127.0.0.1', udp_port))

        # Wait for all ``on_tally_added`` events
        _ = await evt_listener.get()
        while not evt_listener.empty():
            _ = await evt_listener.get()

        screen = receiver.screens[uhs_screen]

        # Check all receiver tallies against the expected ones
        assert len(receiver.tallies) == len(uhs500_msg_parsed.displays)

        for disp in uhs500_msg_parsed.displays:
            assert disp.index in screen
            tally = screen.tallies[disp.index]
            assert tally.id == (uhs_screen, disp.index)
            assert receiver.tallies[tally.id] is tally
            assert disp == tally

        # Change each display and send the updated message to receiver
        # Then wait for ``on_tally_updated`` events
        receiver.unbind(evt_listener)
        receiver.bind_async(loop, on_tally_updated=evt_listener.callback)
        for disp in uhs500_msg_parsed.displays:
            tally = screen.tallies[disp.index]

            for tally_type in TallyType.all():
                attr = tally_type.name
                cur_value = getattr(disp, attr)
                if cur_value == TallyColor.RED:
                    new_value = TallyColor.GREEN
                else:
                    new_value = TallyColor.RED
                setattr(disp, attr, new_value)
            disp.text = f'{disp.text}-foo'
            disp.brightness = 1

            transport.sendto(uhs500_msg_parsed.build_message(),
                             ('127.0.0.1', udp_port))

            evt_args, evt_kwargs = await evt_listener.get()
            evt_tally = evt_args[0]
            assert evt_tally is tally
            assert disp == tally
Exemple #15
0
async def test_with_uhs_data(udp_port):

    loop = asyncio.get_event_loop()

    sender = UmdSender(clients=[('127.0.0.1', udp_port)])
    receiver = UmdReceiver(hostaddr='127.0.0.1', hostport=udp_port)

    evt_listener = EventListener()
    receiver.bind_async(loop, on_tally_added=evt_listener.callback)

    screen_index = 1

    async with receiver:
        async with sender:
            # Create initial tallies using text method
            for i in range(100):
                t_id = (screen_index, i)
                sender.set_tally_text(t_id, f'Tally-{i}')
                tx_tally = sender.tallies[t_id]
                screen = sender.screens[screen_index]
                assert screen[i] is tx_tally

                evt_args, evt_kwargs = await evt_listener.get()
                rx_tally = evt_args[0]
                assert rx_tally == tx_tally

            # Create one more tally using ``set_tally_color``
            t_id = (screen_index, 200)
            sender.set_tally_color(t_id, TallyType.lh_tally, TallyColor.GREEN)
            tx_tally = sender.tallies[t_id]
            assert screen[200] is tx_tally
            evt_args, evt_kwargs = await evt_listener.get()
            rx_tally = evt_args[0]
            assert rx_tally == tx_tally

            # Allow the sender to do a full refresh.  Nothing should have changed
            await asyncio.sleep(sender.tx_interval)
            assert evt_listener.empty()

            # Connect to ``on_tally_updated`` events
            receiver.unbind(evt_listener)
            receiver.bind_async(loop, on_tally_updated=evt_listener.callback)

            # Change each tally/tally_type color to red and check the received values
            for tx_tally in sender.tallies.values():
                for tally_type in TallyType.all():
                    sender.set_tally_color(tx_tally.id, tally_type,
                                           TallyColor.RED)

                    evt_args, evt_kwargs = await evt_listener.get()
                    rx_tally = evt_args[0]
                    assert rx_tally is receiver.tallies[tx_tally.id]
                    assert rx_tally == tx_tally

            # Change the text of the extra tally from above and check
            t_id = (screen_index, 200)
            sender.set_tally_text(t_id, 'Tally-200')
            tx_tally = sender.tallies[t_id]
            evt_args, evt_kwargs = await evt_listener.get()
            rx_tally = evt_args[0]
            assert rx_tally == tx_tally

            # Let the sender to another full refresh
            await asyncio.sleep(sender.tx_interval)
            assert evt_listener.empty()

            # Change all tally/tally_type colors, but don't wait for results yet
            for tx_tally in sender.tallies.values():
                for tally_type in TallyType.all():
                    sender.set_tally_color(tx_tally.id, tally_type,
                                           TallyColor.AMBER)
                sender.set_tally_text(tx_tally.id, f'foo-{tx_tally.index}')

            # Wait for updates from last loop to get to the receiver
            # and check the results
            _ = await evt_listener.get()
            while not evt_listener.empty():
                _ = await evt_listener.get()

            for tx_tally in sender.tallies.values():
                rx_tally = receiver.tallies[tx_tally.id]
                assert rx_tally == tx_tally
Exemple #16
0
def iter_tally_types_and_colors():
    yield from itertools.product(TallyType.all(), iter_tally_colors())
Exemple #17
0
        """The client data as a tuple of (:attr:`hostaddr`, :attr:`hostport`)
        """
        return (self.hostaddr, self.hostport)


ClientsOption = ListOption(
    name='clients', type=ClientData, required=False,
    sub_options=ClientData.get_init_options(),
    title='Clients',
)

ClientOrData = Union[Client, ClientData]
""":data:`~tslumd.sender.Client` or :class:`ClientData`
"""

INDICATOR_PROPS = {tt.name: tt for tt in TallyType.all()}


class UmdOutput(BaseOutput, namespace='umd.UmdOutput', final=True):
    """Networked tally output using the UMDv5 protocol

    Arguments:
        config (MultiTallyConfig): The initial value for
            :attr:`~tallypi.baseio.BaseIO.config`
        clients (Iterable[ClientOrData], optional): The initial :attr:`clients` to set
        all_off_on_close: (bool, optional): Value to set for :attr:`all_off_on_close`

    Properties:
        clients (set): A :class:`~.utils.SetProperty` containing the remote host
            addresses as address/port tuples.
Exemple #18
0
 def from_dict(cls, d: Dict) -> 'SingleTallyConfig':
     kw = d.copy()
     if not isinstance(kw['tally_type'], TallyType):
         kw['tally_type'] = TallyType.from_str(kw['tally_type'])
     return super().from_dict(kw)