#!/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()