Passed
Push — master ( d01dda...94e82a )
by Matt
01:53
created

TransmittingController.__runner()   A

Complexity

Conditions 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 16
rs 9.95
c 0
b 0
f 0
cc 3
nop 1
1
"""
2
 *  PyDMXControl: A Python 3 module to control DMX using OpenDMX or uDMX.
3
 *                Featuring fixture profiles, built-in effects and a web control panel.
4
 *  <https://github.com/MattIPv4/PyDMXControl/>
5
 *  Copyright (C) 2022 Matt Cowley (MattIPv4) ([email protected])
6
"""
7
8
from queue import Queue, Empty
9
from threading import Thread
10
from typing import List
11
12
from ._Controller import Controller
13
14
15
class TransmittingController(Controller):
16
17
    def __init__(self, *args, **kwargs):
18
        super().__init__(*args, **kwargs)
19
20
        # Create the frame queue
21
        self.__queue = Queue()
22
23
        # Track the thread
24
        self.__thread = None
25
26
        # Check if auto-start is enabled
27
        self.__auto = True
28
        if 'autostart' in kwargs:
29
            if isinstance(kwargs['autostart'], bool):
30
                self.__auto = kwargs['autostart']
31
32
        # Run if auto-start
33
        if self.__auto:
34
            self.run()
35
36
    def _connect(self):
37
        pass
38
39
    def _close(self):
40
        pass
41
42
    def _transmit(self, frame: List[int], first: int):
43
        pass
44
45
    def __runner(self):
46
        while True:
47
            # Get next item in queue, or wait for item
48
            item = self.__queue.get()
49
50
            # None is passed to end the runner
51
            if item is None:
52
                self.__queue.task_done()
53
                break
54
55
            # Transmit current frame
56
            self._transmit(*item)
57
            self.__queue.task_done()
58
59
            # Drain any excess frames if transmission is slower than frame rate
60
            self.__drain()
61
62
    def __send(self):
63
        self.__queue.put_nowait((
64
            self.get_frame(),  # Get the DMX channel frame
65
            self.first_channel if self.dynamic_frame else 1,  # Get the first channel if frame is dynamic
66
        ))
67
68
    def __drain(self):
69
        while not self.__queue.empty():
70
            try:
71
                self.__queue.get(False)
72
            except Empty:
73
                continue
74
            self.__queue.task_done()
75
76
    def close(self):
77
        # Drain and stop the thread
78
        if self.__thread:
79
            self.__drain()
80
            self.__queue.put(None)
81
            self.__queue.join()
82
            self.__thread = None
83
84
        # Close the device
85
        self._close()
86
87
        # Parent close
88
        super().close()
89
90
    def run(self):
91
        # Connect the device
92
        self._connect()
93
94
        # Start the thread
95
        self.__thread = Thread(target=self.__runner, daemon=True)
96
        self.__thread.start()
97
98
        # Add the transmission of data to the ticker
99
        self.ticker.add_callback(self.__send, 0)
100