2021-04-08 21:21:28 +00:00
|
|
|
|
2021-04-17 21:51:43 +00:00
|
|
|
from helpers.the_terminator import Terminator
|
2021-04-05 21:13:53 +00:00
|
|
|
from typing import List, Optional
|
2021-04-08 21:21:28 +00:00
|
|
|
from multiprocessing import Queue, current_process
|
2021-02-14 23:58:32 +00:00
|
|
|
import serial
|
2021-04-05 21:13:53 +00:00
|
|
|
import time
|
2021-04-04 21:34:46 +00:00
|
|
|
from setproctitle import setproctitle
|
2021-04-05 21:13:53 +00:00
|
|
|
|
2021-04-08 21:21:28 +00:00
|
|
|
from helpers.logging_manager import LoggingManager
|
|
|
|
from helpers.state_manager import StateManager
|
|
|
|
from controllers.controller import Controller
|
2021-02-14 23:58:32 +00:00
|
|
|
|
2021-04-08 21:32:16 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
class MattchBox(Controller):
|
|
|
|
ser: Optional[serial.Serial]
|
|
|
|
port: Optional[str]
|
|
|
|
next_port: Optional[str]
|
|
|
|
server_state: StateManager
|
|
|
|
logger: LoggingManager
|
2021-02-14 23:58:32 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
def __init__(
|
|
|
|
self, server_to_q: List[Queue], server_from_q: List[Queue], state: StateManager
|
|
|
|
):
|
2021-02-14 23:58:32 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
process_title = "ControllerHandler"
|
|
|
|
setproctitle(process_title)
|
2021-04-18 20:17:41 +00:00
|
|
|
current_process().name = process_title
|
2021-04-05 21:13:53 +00:00
|
|
|
|
|
|
|
self.ser = None
|
2021-04-08 19:53:51 +00:00
|
|
|
self.logger = LoggingManager("ControllerMattchBox")
|
2021-04-05 21:13:53 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
self.server_state = state # This is a copy, will not update :/
|
2021-04-08 21:21:28 +00:00
|
|
|
|
|
|
|
# This doesn't run, the callback function gets lost
|
|
|
|
# due to state being a copy in the multiprocessing process.
|
|
|
|
# self.server_state.add_callback(self._state_handler)
|
|
|
|
|
|
|
|
# Allow server config changes to trigger controller reload if required.
|
2021-04-08 19:53:51 +00:00
|
|
|
self.port = None
|
2021-04-18 19:27:54 +00:00
|
|
|
self.next_port = self.server_state.get()["serial_port"]
|
2021-09-01 21:26:40 +00:00
|
|
|
self.logger.log.info("Server config gives port as: {}".format(self.next_port))
|
2021-04-08 19:53:51 +00:00
|
|
|
|
|
|
|
self.server_from_q = server_from_q
|
|
|
|
self.server_to_q = server_to_q
|
|
|
|
|
|
|
|
self.handler()
|
|
|
|
|
|
|
|
# This doesn't run, the callback function gets lost in StateManager.
|
2021-04-17 21:51:43 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
def _state_handler(self):
|
2021-04-18 19:27:54 +00:00
|
|
|
new_port = self.server_state.get()["serial_port"]
|
2021-04-08 19:53:51 +00:00
|
|
|
self.logger.log.info("Got server config update. New port: {}".format(new_port))
|
|
|
|
if new_port != self.port:
|
|
|
|
self.logger.log.info(
|
|
|
|
"Switching from port {} to {}".format(self.port, new_port)
|
|
|
|
)
|
|
|
|
# The serial port config has changed. Let's reload the serial.
|
|
|
|
self.port = None
|
|
|
|
self.next_port = new_port
|
|
|
|
|
2021-09-01 21:26:40 +00:00
|
|
|
def _disconnected(self):
|
|
|
|
# If we lose the controller, make sure to set channels live, so we tracklist.
|
2021-06-22 17:20:18 +00:00
|
|
|
for i in range(len(self.server_from_q)):
|
|
|
|
self.sendToPlayer(i, "SETLIVE:True")
|
2021-09-01 21:26:40 +00:00
|
|
|
self.server_state.update("ser_connected", False)
|
|
|
|
|
|
|
|
def connect(self, port: Optional[str]):
|
2021-06-22 17:20:18 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
if port:
|
|
|
|
# connect to serial port
|
|
|
|
self.ser = serial.serial_for_url(port, do_not_open=True)
|
|
|
|
self.ser.baudrate = 2400
|
2021-04-05 21:13:53 +00:00
|
|
|
try:
|
2021-04-08 19:53:51 +00:00
|
|
|
self.ser.open()
|
|
|
|
self.logger.log.info("Connected to serial port {}".format(port))
|
|
|
|
except serial.SerialException as e:
|
|
|
|
self.logger.log.error(
|
2021-09-01 21:26:40 +00:00
|
|
|
"Could not open serial port" + str(port),
|
|
|
|
e
|
2021-04-08 19:53:51 +00:00
|
|
|
)
|
2021-09-01 21:26:40 +00:00
|
|
|
self._disconnected()
|
2021-04-08 19:53:51 +00:00
|
|
|
self.ser = None
|
|
|
|
else:
|
|
|
|
self.ser = None
|
2021-02-14 23:58:32 +00:00
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
def handler(self):
|
2021-04-17 21:51:43 +00:00
|
|
|
terminator = Terminator()
|
|
|
|
while not terminator.terminate:
|
2021-04-08 19:53:51 +00:00
|
|
|
if (
|
|
|
|
self.ser and self.ser.is_open and self.port
|
|
|
|
): # If self.port is changing (via state_handler), we should stop.
|
|
|
|
try:
|
|
|
|
line = int.from_bytes(
|
|
|
|
self.ser.read(1), "big"
|
|
|
|
) # Endianness doesn't matter for 1 byte.
|
|
|
|
self.logger.log.info("Received from controller: " + str(line))
|
|
|
|
if line == 255:
|
2021-09-01 21:26:40 +00:00
|
|
|
self.ser.write(b"\xff") # Send 255 back, this is a keepalive.
|
2021-06-22 17:20:18 +00:00
|
|
|
elif line in [51,52,53]:
|
|
|
|
# We've received a status update about fader live status, fader is down.
|
|
|
|
self.sendToPlayer(line-51, "SETLIVE:False")
|
|
|
|
elif line in [61,62,63]:
|
|
|
|
# We've received a status update about fader live status, fader is up.
|
|
|
|
self.sendToPlayer(line-61, "SETLIVE:True")
|
2021-04-08 19:53:51 +00:00
|
|
|
elif line in [1, 3, 5]:
|
2021-05-02 12:52:47 +00:00
|
|
|
self.sendToPlayer(int(line / 2), "PLAYPAUSE")
|
2021-04-08 19:53:51 +00:00
|
|
|
elif line in [2, 4, 6]:
|
|
|
|
self.sendToPlayer(int(line / 2) - 1, "STOP")
|
2021-04-08 21:32:16 +00:00
|
|
|
except Exception:
|
2021-05-02 17:12:38 +00:00
|
|
|
time.sleep(5)
|
|
|
|
self.connect(self.port)
|
2021-04-08 19:53:51 +00:00
|
|
|
finally:
|
|
|
|
time.sleep(0.01)
|
|
|
|
|
|
|
|
elif self.port:
|
|
|
|
# If there's still a port set, just wait a moment and see if it's been reconnected.
|
2021-09-01 21:26:40 +00:00
|
|
|
self._disconnected()
|
2021-04-08 19:53:51 +00:00
|
|
|
time.sleep(10)
|
|
|
|
self.connect(self.port)
|
|
|
|
|
|
|
|
else:
|
|
|
|
# We're not already connected, or a new port connection is to be made.
|
|
|
|
if self.ser:
|
|
|
|
self.ser.close()
|
2021-09-01 21:26:40 +00:00
|
|
|
self._disconnected()
|
2021-04-08 19:53:51 +00:00
|
|
|
|
2021-04-08 21:21:28 +00:00
|
|
|
if self.next_port is not None:
|
2021-04-08 19:53:51 +00:00
|
|
|
self.connect(self.next_port)
|
|
|
|
if self.ser and self.ser.is_open:
|
|
|
|
self.port = (
|
|
|
|
self.next_port
|
|
|
|
) # We connected successfully, make it stick.
|
|
|
|
self.server_state.update("ser_connected", True)
|
|
|
|
continue # skip the sleep.
|
|
|
|
time.sleep(10)
|
|
|
|
|
2021-04-17 21:51:43 +00:00
|
|
|
self.connect(None)
|
|
|
|
|
2021-04-08 19:53:51 +00:00
|
|
|
def sendToPlayer(self, channel: int, msg: str):
|
2021-06-22 17:20:18 +00:00
|
|
|
self.logger.log.info("Sending message to player channel {}: {}".format(channel, msg))
|
2021-04-08 19:53:51 +00:00
|
|
|
self.server_to_q[channel].put("CONTROLLER:" + msg)
|