335 lines
12 KiB
Python
335 lines
12 KiB
Python
import ast
|
|
import math
|
|
import struct
|
|
import sys
|
|
import time
|
|
|
|
import numpy as np
|
|
import redis
|
|
|
|
from libs3 import gstt, log
|
|
|
|
|
|
def pack_point(laser, intensity, x, y, r, g, b, i=-1, u1=0, u2=0, flags=0):
|
|
"""Pack some color values into a struct dac_point."""
|
|
|
|
# print("Tracer", laser,":", r,g,b,"intensity", intensity, "i", i)
|
|
|
|
if r > intensity:
|
|
r = intensity
|
|
if g > intensity:
|
|
g = intensity
|
|
if b > intensity:
|
|
b = intensity
|
|
|
|
if max(r, g, b) == 0:
|
|
i = 0
|
|
else:
|
|
i = intensity
|
|
|
|
x = int(x)
|
|
y = int(y)
|
|
# print("Tracer ", laser, ": packing", x, y, r, g, b, "intensity", intensity, "i", i)
|
|
|
|
if x < -32767:
|
|
if gstt.debug > 1:
|
|
log.err("Tracer " + str(laser) + " : x coordinates " + str(x) + " was below -32767")
|
|
x = -32000
|
|
|
|
if x > 32767:
|
|
if gstt.debug > 1:
|
|
log.err("Tracer " + str(laser) + " : x coordinates " + str(x) + " was bigger than 32767")
|
|
x = 32000
|
|
|
|
if y < -32767:
|
|
|
|
if gstt.debug > 1:
|
|
log.err("Tracer " + str(laser) + " : y coordinates " + str(y) + " was below -32767")
|
|
y = -32000
|
|
|
|
if y > 32767:
|
|
|
|
if gstt.debug > 1:
|
|
log.err("Tracer " + str(laser) + " : y coordinates " + str(y) + " was bigger than 32767")
|
|
y = 32000
|
|
|
|
return struct.pack("<HhhHHHHHH", flags, x, y, r, g, b, i, u1, u2)
|
|
|
|
|
|
class ProtocolError(Exception):
|
|
"""Exception used when a protocol error is detected."""
|
|
pass
|
|
|
|
|
|
class Status(object):
|
|
"""Represents a status response from the DAC."""
|
|
|
|
def __init__(self, data):
|
|
"""Initialize from a chunk of data."""
|
|
self.protocol_version, self.le_state, self.playback_state, \
|
|
self.source, self.le_flags, self.playback_flags, \
|
|
self.source_flags, self.fullness, self.point_rate, \
|
|
self.point_count = \
|
|
struct.unpack("<BBBBHHHHII", data)
|
|
|
|
def dump(self, prefix=" - "):
|
|
"""Dump to a string."""
|
|
lines = [
|
|
""
|
|
"Host ",
|
|
"Light engine: state %d, flags 0x%x" %
|
|
(self.le_state, self.le_flags),
|
|
"Playback: state %d, flags 0x%x" %
|
|
(self.playback_state, self.playback_flags),
|
|
"Buffer: %d points" %
|
|
(self.fullness,),
|
|
"Playback: %d kpps, %d points played" %
|
|
(self.point_rate, self.point_count),
|
|
"Source: %d, flags 0x%x" %
|
|
(self.source, self.source_flags)
|
|
]
|
|
|
|
if gstt.debug == 2:
|
|
print()
|
|
for l in lines:
|
|
print(prefix + l)
|
|
|
|
|
|
def OnePointIterator(ref):
|
|
while True:
|
|
for indexpoint, currentpoint in enumerate(ref.pl):
|
|
|
|
# We modify the point geometry according to warp settings
|
|
# print indexpoint, currentpoint
|
|
# xyc = [currentpoint[0], currentpoint[1], currentpoint[2]]
|
|
# ref.xyrgb = ref.get_warped_point(xyc)
|
|
x, y = ref.get_warped_point(currentpoint[0], currentpoint[1])
|
|
r, g, b = ref.int_to_rgb(currentpoint[2])
|
|
|
|
# We modify the point color based on channel specific r,g,b intensity settings
|
|
rgb = (round(r * ref.intred / 100), round(g * ref.intgreen / 100),
|
|
round(b * ref.intblue / 100))
|
|
|
|
# We compute the delta with previous position
|
|
delta_x, delta_y = x - ref.prev_x, y - ref.prev_y
|
|
|
|
# We compute the distance from previous postion
|
|
if math.hypot(delta_x, delta_y) < 4000:
|
|
# For glitch art : decrease lsteps
|
|
# l_steps = [ (1.0, 8)]
|
|
l_steps = gstt.stepshortline
|
|
else:
|
|
# For glitch art : decrease lsteps
|
|
# l_steps = [ (0.25, 3), (0.75, 3), (1.0, 10)]
|
|
l_steps = gstt.stepslongline
|
|
|
|
# We add intermediate points based on settings
|
|
for e in l_steps:
|
|
step = e[0]
|
|
|
|
for i in range(0, e[1]):
|
|
ref.xyrgb_step = (ref.prev_x + step * delta_x,
|
|
ref.prev_y + step * delta_y) + rgb # + ref.xyrgb_prev[2:]# + rgb
|
|
# print(ref.xyrgb_step)
|
|
yield ref.xyrgb_step
|
|
|
|
ref.prev_x, ref.prev_y = x, y
|
|
ref.xyrgb_prev = [x, y]
|
|
|
|
|
|
class Tracer(object):
|
|
"""A connection to a DAC."""
|
|
laser_id: int
|
|
redis: object
|
|
point_list_number: list
|
|
pl: list
|
|
client_key: str # /pl/<laser_id> by default
|
|
prev_x = 0
|
|
prev_y = 0
|
|
|
|
black_points = [(278.0, 225.0, 0), (562.0, 279.0, 0), (401.0, 375.0, 0), (296.0, 454.0, 0), (298.0, 165.0, 0)]
|
|
grid_points = [(300.0, 200.0, 0), (500.0, 200.0, 65280), (500.0, 400.0, 65280), (300.0, 400.0, 65280),
|
|
(300.0, 200.0, 65280), (300.0, 200.0, 0), (200.0, 100.0, 0), (600.0, 100.0, 65280),
|
|
(600.0, 500.0, 65280), (200.0, 500.0, 65280), (200.0, 100.0, 65280)]
|
|
ackstate = {'61': 'ACK', '46': 'FULL', '49': "INVALID", '21': 'STOP', '64': "NO CONNECTION ?",
|
|
'35': "NO CONNECTION ?",
|
|
'97': 'ACK', '70': 'FULL', '73': "INVALID", '33': 'STOP', '100': "NOCONNECTION", '48': "NOCONNECTION",
|
|
'a': 'ACK', 'F': 'FULL', 'I': "INVALID", '!': 'STOP', 'd': "NO CONNECTION ?", '0': "NO CONNECTION ?"}
|
|
lstate = {'0': 'IDLE', '1': 'PREPARE', '2': "PLAYING", '3': 'STOP', '64': "NOCONNECTION ?"}
|
|
|
|
"""
|
|
status : the general status of the DAC, is it working or not?
|
|
ack_status : DAC specific, sent continuously by the DAC to track activity
|
|
"""
|
|
|
|
def int_to_rgb(self, c):
|
|
return ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8
|
|
|
|
def get_status(self):
|
|
raise Exception("Please override the method")
|
|
|
|
def set_status(self, status: int):
|
|
raise Exception("Please override the method")
|
|
|
|
def get_warped_point(self, x, y):
|
|
"""
|
|
A DAC specific point model, ex EtherPoint or HeliosPoint
|
|
:param xyc: x,y,color
|
|
:return:
|
|
"""
|
|
raise Exception("Please override the method")
|
|
|
|
# def get_ack_status(self):
|
|
# raise Exception("Please override the method")
|
|
#
|
|
# def set_ack_status(self, ack_status: int):
|
|
# raise Exception("Please override the method")
|
|
|
|
def prepare(self):
|
|
raise Exception("Please override the method")
|
|
|
|
def begin(self, n, kpps):
|
|
raise Exception("Please override the method")
|
|
|
|
def before_loop(self):
|
|
"""
|
|
Hook for DAC specific actions on each loop
|
|
:return:
|
|
"""
|
|
raise Exception("Please override the method")
|
|
|
|
def get_points_capacity(self):
|
|
"""
|
|
How much points can we send next to the DAC
|
|
:return: int
|
|
"""
|
|
raise Exception("Please override the method")
|
|
|
|
def convert_color(self, c):
|
|
"""
|
|
DAC specific color conversion
|
|
:param c:
|
|
:return:
|
|
"""
|
|
raise Exception("Please override the method")
|
|
|
|
def write(self, points):
|
|
"""
|
|
Actual sending to the DAC
|
|
:param points: a list of points "corrected" with geometry, color, intensity settings
|
|
:return:
|
|
"""
|
|
raise Exception("Please override the method")
|
|
|
|
def play_stream(self):
|
|
"""
|
|
Main loop common for every dac driver
|
|
:return:
|
|
"""
|
|
self.before_loop()
|
|
|
|
started = 0
|
|
|
|
while True:
|
|
|
|
self.redis.set('/lstate/' + str(self.laser_id), self.get_status())
|
|
|
|
# print("laser", self.laser_id, "Pb : ",self.last_status.playback_state)
|
|
|
|
order = int(self.redis.get('/order/' + str(self.laser_id)).decode('ascii'))
|
|
# print("tracer", str(self.laser_id),"order", order, type(order)
|
|
|
|
if order == 0:
|
|
""" 0 : The actual points sending to laser """
|
|
# USER point list /
|
|
# @todo si la clef est vide utiliser les points noirs ? -> syntax error -> black points.
|
|
try:
|
|
self.pl = ast.literal_eval(self.redis.get(self.clientkey + str(self.laser_id)).decode('ascii'))
|
|
# print(self.clientkey + str(self.laser_id))
|
|
# print(self.pl)
|
|
|
|
except SyntaxError:
|
|
print("BAD POINTLIST on Tracer : laser", self.laser_id, " order 0 : pl : ", self.pl)
|
|
self.pl = self.black_points
|
|
|
|
# print("Tracer : laser", self.laser_id, " order 0 : pl : ",len(self.pl))
|
|
|
|
else:
|
|
|
|
if order == 1:
|
|
""" 1 : Get the new EDH / The zoom || trapezoidal / homography settings for the laser """
|
|
print("Tracer", self.laser_id, "new EDH ORDER in redis")
|
|
gstt.EDH[self.laser_id] = np.array(
|
|
ast.literal_eval(self.redis.get('/EDH/' + str(self.laser_id)).decode('ascii')))
|
|
# Back to user point list
|
|
self.redis.set('/order/' + str(self.laser_id), 0)
|
|
|
|
#
|
|
if order == 2:
|
|
""" 2 : Send a BLACK point list """
|
|
print("Tracer", self.laser_id, "BLACK ORDER in redis")
|
|
self.pl = self.black_points
|
|
|
|
if order == 3:
|
|
""" 3: Send a GRID point list"""
|
|
print("Tracer", self.laser_id, "GRID ORDER in redis")
|
|
self.pl = self.grid_points
|
|
|
|
if order == 4:
|
|
""" 4: Resampler Change, modify the automatic intermediary points settings """
|
|
self.resampler = ast.literal_eval(
|
|
self.redis.get('/resampler/' + str(self.laser_id)).decode('ascii'))
|
|
print("Tracer", self.laser_id, " : resetting lsteps for", self.resampler)
|
|
gstt.stepshortline = self.resampler[0]
|
|
gstt.stepslongline[0] = self.resampler[1]
|
|
gstt.stepslongline[1] = self.resampler[2]
|
|
gstt.stepslongline[2] = self.resampler[3]
|
|
# Back to user point list order
|
|
self.redis.set('/order/' + str(self.laser_id), 0)
|
|
|
|
if order == 5:
|
|
""" 5: Client Key change, change the address to read points from in redis ex: /pl/0 => /pl/3"""
|
|
print("Tracer", self.laser_id, "new clientkey")
|
|
self.clientkey = self.redis.get('/clientkey')
|
|
# Back to user point list order
|
|
self.redis.set('/order/' + str(self.laser_id), 0)
|
|
|
|
if order == 6:
|
|
""" 6: change intensity """
|
|
# @todo check for helios vs etherdream
|
|
self.intensity = int(self.redis.get('/intensity/' + str(self.laser_id)).decode('ascii')) << 8
|
|
print("Tracer", self.laser_id, "new Intensity", self.intensity)
|
|
gstt.intensity[self.laser_id] = self.intensity
|
|
self.redis.set('/order/' + str(self.laser_id), "0")
|
|
|
|
if order == 7:
|
|
""" 7: kpps change"""
|
|
gstt.kpps[self.laser_id] = int(self.redis.get('/kpps/' + str(self.laser_id)).decode('ascii'))
|
|
print("Tracer", self.laser_id, "new kpps", gstt.kpps[self.laser_id])
|
|
self.update(0, gstt.kpps[self.laser_id])
|
|
self.redis.set('/order/' + str(self.laser_id), "0")
|
|
|
|
if order == 8:
|
|
""" 8: color balance change"""
|
|
self.intred = int(self.redis.get('/red/' + str(self.laser_id)).decode('ascii'))
|
|
self.intgreen = int(self.redis.get('/green/' + str(self.laser_id)).decode('ascii'))
|
|
self.intblue = int(self.redis.get('/blue/' + str(self.laser_id)).decode('ascii'))
|
|
print("Tracer", self.laser_id, "new color balance", self.intred, "% ", self.intgreen, "% ",
|
|
self.intblue, "% ")
|
|
self.redis.set('/order/' + str(self.laser_id), "0")
|
|
|
|
# if getattr(self, "last_status") :
|
|
# self.redis.set('/lstt/' + str(self.laser_id), self.last_status.playback_state)
|
|
# pdb.set_trace()
|
|
# How much room?
|
|
capacity = self.get_points_capacity()
|
|
iterator = iter(OnePointIterator(self))
|
|
|
|
self.redis.set('/cap/' + str(self.laser_id), capacity)
|
|
points = [next(iterator) for i in range(capacity)]
|
|
self.write(points)
|
|
if not started:
|
|
print("Tracer", self.laser_id, "starting with", gstt.kpps[self.laser_id], "kpps")
|
|
self.begin(0, gstt.kpps[self.laser_id])
|
|
started = 1
|