DMX-Hat_RasPi/Python/sock.py

331 lines
7.3 KiB
Python

#!/usr/bin/env python
# WS server example that synchronizes state across clients
import asyncio
# import json
import logging
import websockets
# import redis
import time
import serial
import numpy as np
#import RPi.GPIO as GPIO
import threading
# Colordefinition for console print
class pcol:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
#################################################################################
#
#
# G L O B A L S
#
# DMX array config
DMXDATA = np.zeros([513], dtype='uint8')
# DMX Startcode
DMXDATA[0] = 0 # 0 = Lighting Control Data
# DMX Reset [µs]/float
DMXRST = 88.0 # typical 88µs
# DMX Break [µs]/float
DMXBRK = 8.0 # 8µs (min) to 1s (max)
# DMX Sequence sleep [ms]/float
DMXSLP = 250.0 # 0µs (min) to 1s (max)
# Buffer for RDM
DMXRDM = 0
DMXRDMFLAG = False # 1 for radlines() is active to buffer rdm packages
#
# Websocket variables
#
# Set to store user-data
USERS = set()
# Set to store other data served by the websocket
# STATE = {}
# DummyFill
# for i in range(1, 10):
# STATE.update({str(i): 0})
#################################################################################
#
#
# F U N C T I O N S
#
#
###########################
#
# DMX
#
###########################
def sendDMX(data):
#continuously sending the signal; to receive changed values: Threading
global DMXRDMFLAG
global DMXRDM
while False:#True:
with serial.Serial('/dev/serial0', baudrate=250000, timeout=1, stopbits=serial.STOPBITS_TWO, bytesize=8) as DMXSER:# UART configuration and definition
DMXSER.cancel_read()
# GPIO_18 to switch RS-485 driver (IC1) to TX-mode
#GPIO.output(18, GPIO.HIGH)
# send reset
DMXSER.send_break(DMXRST / 1000000.0)
# break-time
time.sleep(DMXBRK / 1000000.0)
# send complete data array
DMXSER.write(bytearray(DMXDATA))
# GPIO_18 to switch RS-485 driver (IC1) to RX-mode
DMXSER.reset_input_buffer()
#GPIO.output(18, GPIO.LOW)
# Sleep between TX packages, use time to get RDM packages
DMXRDM = ""
DMXRDMFLAG = False
DMXSER.timeout(DMXSLP / 1000.0) # between 0 - 1 sec
DMXRDM = DMXSER.readlines()
DMXRDMFLAG = True
###########################
#
# THREADING
#
###########################
slicy = threading.Thread(target=sendDMX, args=(DMXDATA, ))
slicy.start()
###########################
#
# Websockset
#
###########################
async def rdm_state():
global DMXRDM
global DMXRDMFLAG
if USERS and DMXRDMFLAG:
message = "rdm=" + DMXRDM + ";"
print("RDM EVENT " + DMXRDM)
await asyncio.wait([user.send(message) for user in USERS])
# Notify_State is triggered each time a new connection is established to the websocket
# and only sent to the new user, not to all!
# Serving all infos once as complete string, so we only have to send delta from that time on
async def notify_state():
if USERS: # asyncio.wait doesnt accept an empty list
message = state_event()
await asyncio.wait([user.send(message) for user in USERS])
# Generate message-text for Notify_State
def state_event():
# we have to send a string
rtn = ""
# dont have to send DMXDATA[0], this is the start code
for index in range(1, len(DMXDATA)):
rtn += (str(index) + "=" + str(DMXDATA[index]) + ";")
# give debug feedback
print("state_event returned ", rtn)
return rtn
# Users-Event is triggered each time a new connection is established to the websocket
# and sent to all other but not the new user
async def notify_users():
if USERS: # asyncio.wait doesnt accept an empty list
message = users_event()
await asyncio.wait([user.send(message) for user in USERS])
# Generate message-text for Notify_Users
def users_event():
# only send the new value
return "users=" + str(len(USERS)) + ";"
# Handling (un-)registering
async def register(websocket):
USERS.add(websocket)
print("New user registered ")
await notify_users()
async def unregister(websocket):
USERS.remove(websocket)
print("Known user left")
await notify_users()
#
# Sending delta-reports to all users when a value changed except user count
#
async def notify_delta(delta):
if USERS: # asyncio.wait doesnt accept an empty list
await asyncio.wait([user.send(delta) for user in USERS])
#
# Websocket working thread
#
async def websworker(websocket, path):
# register(websocket) sends user_event() to websocket
await register(websocket)
try:
await websocket.send(state_event())
while True:
try:
# waiting for a new message received on the websocket
message = await asyncio.wait_for(websocket.recv(), timeout=0.1)
# split message from multiple commands in one string
data = message.split(";")
# process each command
for index in range(len(data)):
# act[0] will be the key
# act[1] will be the value
act = data[index].split("=")
try:
# if key is a number, we will have e change on a dmx value
# otherwise, we have a string to check which will be processed in except...
# poor python - there is no other chance to check if a string can be a int without errors
dmxchannel = int(act[0])
dmxvalue = int(act[1])
# We have integer, but are they in a valid range?
if( dmxchannel < 1 or dmxchannel > 512 or dmxvalue < 0 or dmxvalue > 255):
# this must be an exception, we will continue in the exception-area anyway now
dmxchannel = int("will raise")
# looks valid, so take them to the buffer
DMXDATA[dmxchannel] = dmxvalue
# notifying our connected users
# we can send the resceived command back since the syntax is matched :)
await notify_delta(data[index])
# debug
print("DMX value for channel " + act[0] + " changed to " + act[1])
# It does not look like we have a dmx key-value, so we have to process the command
except Exception:
if act[0] == "manual":
sendDMX(DMXDATA)
print("Manual send data: ", bytearray(DMXDATA))
elif act[0] == "sec":
print("")
else:
print("received message, but can not handle: ", act)
except asyncio.TimeoutError:
await rdm_state()
finally:
await unregister(websocket)
###########################
#
# GPIO
#
###########################
# We have to set GPIO
#GPIO.setmode(GPIO.BCM)
#GPIO.setwarnings(False)
#GPIO.setup(18, GPIO.OUT)
#GPIO.setup(4, GPIO.OUT)
# GPIO_18 to switch RS-485 driver (IC1) to RX-mode
#GPIO.output(18, GPIO.LOW)
# Powercycle 3V3 on HAT to ensure a cleaned up buffer
#GPIO.output(4, GPIO.HIGH)
#time.sleep(0.25)
#GPIO.output(4, GPIO.LOW)
###########################
#
# Start the websocket
#
###########################
asyncio.get_event_loop().run_until_complete(
websockets.serve(websworker, port=6789))
asyncio.get_event_loop().run_forever()