LJ/libs3/tracer_common.py

364 lines
13 KiB
Python

import ast
import math
import struct
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)
class OnePointIterator( ):
def __init__(self, ref):
self.ref = ref
def __iter__(self):
return self
def __next__(self):
while True:
# pdb.set_trace()
for indexpoint, currentpoint in enumerate(self.ref.pl):
# We modify the point geometry according to warp settings
# print indexpoint, currentpoint
# xyc = [currentpoint[0], currentpoint[1], currentpoint[2]]
# self.ref.xyrgb = self.ref.get_warped_point(xyc)
x, y = self.ref.get_warped_point(currentpoint[0], currentpoint[1])
r, g, b = self.ref.int_to_rgb(currentpoint[2])
# We modify the point color based on channel specific r,g,b intensity settings
rgb = (round(r * self.ref.intred / 100), round(g * self.ref.intgreen / 100),
round(b * self.ref.intblue / 100))
# We compute the delta with previous position
delta_x, delta_y = x - self.ref.prev_x, y -self.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]):
self.ref.xyrgb_step = (self.ref.prev_x + step * delta_x,
self.ref.prev_y + step * delta_y) + rgb # + self.ref.xyrgb_prev[2:]# + rgb
print(self.ref.xyrgb_step)
yield self.ref.xyrgb_step
self.ref.prev_x, self.ref.prev_y = x, y
self.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
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 GetPoints(self, capacity):
"""
Iterates points and sends them to OnePoint
:param capacity:
:return:
"""
iterator = OnePointIterator(self)
# i = list(next(iterator))
# d = [list(next(iterator)) for i in range(capacity)]
d = [list(next(iterator)) for i in range(capacity)]
return d
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'))
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()
self.redis.set('/cap/' + str(self.laser_id), capacity)
iterator = OnePointIterator(self)
points = [next(iterator) for i in range(capacity)]
# points = self.GetPoints(capacity)
# print("Writing %d points" % (cap, ))
# t0 = time.time()
# if self.laser_id == 2:
# print(points)
self.write(points)
# t1 = time.time()
# print("Took %f" % (t1 - t0, )
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