Compare commits
9 Commits
master
...
feat/dac_h
Author | SHA1 | Date | |
---|---|---|---|
|
bc19e77f4f | ||
|
6c73bd9fc2 | ||
|
80c76bfeb1 | ||
|
f67e7992ba | ||
|
1847c8ebef | ||
|
88998abde7 | ||
|
506616cb1b | ||
|
a9268b0d73 | ||
|
98538662b8 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,3 +1,5 @@
|
|||||||
.*swp*
|
.*swp*
|
||||||
*__pycache__
|
*__pycache__
|
||||||
www/config.js
|
www/config.js
|
||||||
|
.venv/
|
||||||
|
.idea
|
||||||
|
5
LJ.conf
5
LJ.conf
@ -1,15 +1,16 @@
|
|||||||
[General]
|
[General]
|
||||||
lasernumber = 1
|
lasernumber = 1
|
||||||
debug = 1
|
debug = 2
|
||||||
ljayserverip = 0.0.0.0
|
ljayserverip = 0.0.0.0
|
||||||
wwwip = 127.0.0.1
|
wwwip = 127.0.0.1
|
||||||
nozoscip = 127.0.0.1
|
nozoscip = 127.0.0.1
|
||||||
bhoroscip = 127.0.0.1
|
bhoroscip = 127.0.0.1
|
||||||
autostart = artnet
|
autostart =
|
||||||
wstype = ws
|
wstype = ws
|
||||||
wsport = 9001
|
wsport = 9001
|
||||||
|
|
||||||
[laser0]
|
[laser0]
|
||||||
|
dac_family = helios
|
||||||
color = -1
|
color = -1
|
||||||
type = DS1000
|
type = DS1000
|
||||||
ip = 127.0.0.1
|
ip = 127.0.0.1
|
||||||
|
@ -17,7 +17,7 @@ from /team/laser
|
|||||||
#ConfigName = "setexample.conf"
|
#ConfigName = "setexample.conf"
|
||||||
ConfigName = "LJ.conf"
|
ConfigName = "LJ.conf"
|
||||||
|
|
||||||
debug = 0
|
debug = 10
|
||||||
ljpath=''
|
ljpath=''
|
||||||
|
|
||||||
anims= [[],[],[],[]]
|
anims= [[],[],[],[]]
|
||||||
@ -68,6 +68,7 @@ GridDisplay = [0,0,0,0]
|
|||||||
|
|
||||||
# Transformation Matrix for each laser
|
# Transformation Matrix for each laser
|
||||||
EDH = [[], [], [], []]
|
EDH = [[], [], [], []]
|
||||||
|
# EDH = [[], [], [], []]
|
||||||
|
|
||||||
# Etherdreams reports
|
# Etherdreams reports
|
||||||
# ipconn is initial newdac to its etherdream
|
# ipconn is initial newdac to its etherdream
|
||||||
|
@ -150,13 +150,13 @@ def find_affine(points1,points2):
|
|||||||
H[2,2] = 1
|
H[2,2] = 1
|
||||||
return H
|
return H
|
||||||
|
|
||||||
def apply(H,points):
|
|
||||||
|
|
||||||
p = np.ones((len(points),3),'float64')
|
def apply(H, points):
|
||||||
p[:,:2] = points
|
p = np.ones((len(points), 3), 'float64')
|
||||||
pp = np.dot(p,H.T)
|
p[:, :3] = points
|
||||||
pp[:,:2]/=pp[:,2].reshape(len(p),1)
|
pp = np.dot(p, H.T)
|
||||||
return pp[:,:2]
|
pp[:, :2] /= pp[:, 2].reshape(len(p), 1)
|
||||||
|
return pp[:, :2]
|
||||||
|
|
||||||
# Align and axis swap corrections
|
# Align and axis swap corrections
|
||||||
# Reference points
|
# Reference points
|
||||||
|
BIN
libs3/libHeliosDacAPI.so
Normal file
BIN
libs3/libHeliosDacAPI.so
Normal file
Binary file not shown.
94
libs3/tracer.py
Normal file
94
libs3/tracer.py
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# -*- mode: Python -*-
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
Tracer v0.8.2
|
||||||
|
|
||||||
|
Etherdream DACs handler on network via Redis
|
||||||
|
|
||||||
|
LICENCE : CC
|
||||||
|
Sam Neurohack, pclf
|
||||||
|
|
||||||
|
One tracer process is launched per requested laser by LJ. Lasers parameters in LJ.conf.
|
||||||
|
Live I/O based on redis keys : inputs (Pointlists to draw,...) and outputs (DAC state, errors,..).
|
||||||
|
Keys are mostly read and set at each main loop.
|
||||||
|
|
||||||
|
* Redis keys reference *
|
||||||
|
|
||||||
|
- Drawing things :
|
||||||
|
|
||||||
|
/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber
|
||||||
|
/resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules.
|
||||||
|
the first tuple (1.0,8) is for short line < 4000 in etherdream space
|
||||||
|
(0.25,3),(0.75,3),(1.0,10) for long line > 4000
|
||||||
|
i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream
|
||||||
|
/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis
|
||||||
|
/EDH/lasernumber
|
||||||
|
|
||||||
|
- Tracer control :
|
||||||
|
|
||||||
|
/order 0-8 Set redis key with new value then issue the order number
|
||||||
|
|
||||||
|
0 : Draw Normal point list
|
||||||
|
1 : Get the new EDH = reread redis key /EDH/lasernumber
|
||||||
|
2 : Draw BLACK point list
|
||||||
|
3 : Draw GRID point list
|
||||||
|
4 : Resampler Change (longs and shorts lsteps)
|
||||||
|
5 : Client Key Change = reread redis key /clientkey
|
||||||
|
6 : Max Intensity Change = reread redis key /intensity
|
||||||
|
7 : kpps change = reread redis key /kpps
|
||||||
|
8 : color balance change = reread redis keys /red /green /blue
|
||||||
|
|
||||||
|
|
||||||
|
- Managing Etherdream DACs :
|
||||||
|
|
||||||
|
Discrete drawing values
|
||||||
|
|
||||||
|
/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle
|
||||||
|
/intensity 0-255 Laser output power, then order 6 (for alignement,...)
|
||||||
|
/red 0-100 % of full red, then order 8
|
||||||
|
/green 0-100 % of full green, then order 8
|
||||||
|
/blue 0-100 % of full blue, then order 8
|
||||||
|
|
||||||
|
DAC status report
|
||||||
|
|
||||||
|
/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
|
||||||
|
/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799)
|
||||||
|
/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
|
||||||
|
|
||||||
|
|
||||||
|
Geometric corrections
|
||||||
|
|
||||||
|
Doctodo
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import struct
|
||||||
|
# from gstt import debug
|
||||||
|
from libs3 import gstt, log
|
||||||
|
import math
|
||||||
|
from itertools import cycle
|
||||||
|
# from globalVars import *
|
||||||
|
import pdb
|
||||||
|
import ast
|
||||||
|
import redis
|
||||||
|
from .tracer_etherdream import TracerEtherdream
|
||||||
|
from .tracer_helios import TracerHelios
|
||||||
|
|
||||||
|
from libs3 import homographyp
|
||||||
|
import numpy as np
|
||||||
|
import binascii
|
||||||
|
|
||||||
|
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
|
||||||
|
|
||||||
|
|
||||||
|
def DAC(mylaser, point_list_number, dac_family="etherdream"):
|
||||||
|
print(f"# Starting DAC #{mylaser} PL#:{point_list_number} Family:'{dac_family}'")
|
||||||
|
if dac_family == "etherdream":
|
||||||
|
return TracerEtherdream(mylaser, point_list_number, redis=r, port=7765)
|
||||||
|
if dac_family == "helios":
|
||||||
|
return TracerHelios(mylaser, point_list_number, redis=r)
|
334
libs3/tracer_common.py
Normal file
334
libs3/tracer_common.py
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
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
|
400
libs3/tracer_etherdream.py
Normal file
400
libs3/tracer_etherdream.py
Normal file
@ -0,0 +1,400 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# -*- mode: Python -*-
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
Tracer v0.8.2
|
||||||
|
|
||||||
|
Etherdream DACs handler on network via Redis
|
||||||
|
|
||||||
|
LICENCE : CC
|
||||||
|
Sam Neurohack, pclf
|
||||||
|
|
||||||
|
Includes live conversion in etherdream coordinates, geometric corrections, color balance change, intensity limitation, grid display,...
|
||||||
|
|
||||||
|
One tracer process is launched per requested laser by LJ. Lasers parameters in LJ.conf.
|
||||||
|
Live I/O based on redis keys : inputs (Pointlists to draw,...) and outputs (DAC state, errors,..).
|
||||||
|
Keys are mostly read and set at each main loop.
|
||||||
|
This tracer include an enhanced version (support for several lasers) of the etherdream python library from j4cDAC.
|
||||||
|
|
||||||
|
|
||||||
|
* Redis keys reference *
|
||||||
|
|
||||||
|
- Drawing things :
|
||||||
|
|
||||||
|
/pl/Scene/lasernumber [(x,y,color),(x1,y1,color),...] The live list of drawn pygame points. Tracer continously ask redis for key /clientkey+lasernumber
|
||||||
|
/resampler/lasernumber [(1.0,8), (0.25,3),(0.75,3),(1.0,10)] : a string for resampling rules.
|
||||||
|
the first tuple (1.0,8) is for short line < 4000 in etherdream space
|
||||||
|
(0.25,3),(0.75,3),(1.0,10) for long line > 4000
|
||||||
|
i.e (0.25,3) means go at 25% position on the line, send 3 times this position to etherdream
|
||||||
|
/clientkey "/pl/SceneNumber/" What Scene to retrieve from redis
|
||||||
|
/EDH/lasernumber
|
||||||
|
|
||||||
|
- Tracer control :
|
||||||
|
|
||||||
|
/order 0-8 Set redis key with new value then issue the order number
|
||||||
|
|
||||||
|
0 : Draw Normal point list
|
||||||
|
1 : Get the new EDH = reread redis key /EDH/lasernumber
|
||||||
|
2 : Draw BLACK point list
|
||||||
|
3 : Draw GRID point list
|
||||||
|
4 : Resampler Change (longs and shorts lsteps)
|
||||||
|
5 : Client Key Change = reread redis key /clientkey
|
||||||
|
6 : Max Intensity Change = reread redis key /intensity
|
||||||
|
7 : kpps change = reread redis key /kpps
|
||||||
|
8 : color balance change = reread redis keys /red /green /blue
|
||||||
|
|
||||||
|
|
||||||
|
- Managing Etherdream DACs :
|
||||||
|
|
||||||
|
Discrete drawing values
|
||||||
|
|
||||||
|
/kpps 0- DAC output speed to laser, then order 7. Depends of actual angle
|
||||||
|
/intensity 0-255 Laser output power, then order 6 (for alignement,...)
|
||||||
|
/red 0-100 % of full red, then order 8
|
||||||
|
/green 0-100 % of full green, then order 8
|
||||||
|
/blue 0-100 % of full blue, then order 8
|
||||||
|
|
||||||
|
DAC status report
|
||||||
|
|
||||||
|
/lstt/lasernumber etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
|
||||||
|
/cap/lasernumber number of empty points sent to fill etherdream buffer (up to 1799)
|
||||||
|
/lack/lasernumber "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
|
||||||
|
|
||||||
|
|
||||||
|
Geometric corrections
|
||||||
|
|
||||||
|
Doctodo
|
||||||
|
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
import socket
|
||||||
|
import time
|
||||||
|
import struct
|
||||||
|
# from gstt import debug
|
||||||
|
from libs3 import gstt, log
|
||||||
|
import math
|
||||||
|
import ast
|
||||||
|
|
||||||
|
from libs3 import homographyp
|
||||||
|
import numpy as np
|
||||||
|
from .tracer_common import *
|
||||||
|
|
||||||
|
# @todo this needs normallization
|
||||||
|
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 ?"}
|
||||||
|
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)]
|
||||||
|
|
||||||
|
|
||||||
|
class TracerEtherdream(Tracer):
|
||||||
|
"""A connection to a DAC."""
|
||||||
|
|
||||||
|
# "Laser point List" Point generator
|
||||||
|
# each points is yielded : Getpoints() call n times OnePoint()
|
||||||
|
|
||||||
|
def __init__(self, mylaser, PL, redis, port=7765):
|
||||||
|
"""Connect to the DAC over TCP."""
|
||||||
|
socket.setdefaulttimeout(2)
|
||||||
|
self.redis = redis
|
||||||
|
self.mylaser = mylaser
|
||||||
|
self.clientkey = self.redis.get("/clientkey").decode('ascii')
|
||||||
|
|
||||||
|
# log.info("Tracer "+str(self.mylaser)+" connecting to "+ gstt.lasersIPS[mylaser])
|
||||||
|
# print("DAC", self.mylaser, "Handler process, connecting to", gstt.lasersIPS[mylaser] )
|
||||||
|
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
self.connstatus = self.conn.connect_ex((gstt.lasersIPS[mylaser], port))
|
||||||
|
if self.connstatus == 35 or self.connstatus == 64:
|
||||||
|
log.err(
|
||||||
|
"Tracer " + str(self.mylaser) + " (" + gstt.lasersIPS[mylaser] + "): " + ackstate[str(self.connstatus)])
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"Tracer " + str(self.mylaser) + " (" + gstt.lasersIPS[mylaser] + "): " + ackstate[str(self.connstatus)])
|
||||||
|
|
||||||
|
# ipconn state is -1 at startup (see gstt) and modified here
|
||||||
|
self.redis.set('/lack/' + str(self.mylaser), self.connstatus)
|
||||||
|
gstt.lstt_ipconn[self.mylaser] = self.connstatus
|
||||||
|
|
||||||
|
self.buf = b''
|
||||||
|
# Upper case PL is the Point List number
|
||||||
|
self.PL = PL
|
||||||
|
|
||||||
|
# Lower case pl is the actual point list coordinates
|
||||||
|
|
||||||
|
# pdb.set_trace()
|
||||||
|
self.pl = ast.literal_eval(self.redis.get(self.clientkey + str(self.mylaser)).decode('ascii'))
|
||||||
|
if self.redis.get('/EDH/' + str(self.mylaser)) == None:
|
||||||
|
# print("Laser",self.mylaser,"NO EDH !! Computing one...")
|
||||||
|
homographyp.newEDH(self.mylaser)
|
||||||
|
else:
|
||||||
|
|
||||||
|
gstt.EDH[self.mylaser] = np.array(ast.literal_eval(self.redis.get('/EDH/' + str(self.mylaser)).decode('ascii')))
|
||||||
|
# print("Laser",self.mylaser,"found its EDH in redis")
|
||||||
|
# print gstt.EDH[self.mylaser]
|
||||||
|
|
||||||
|
self.xyrgb = self.xyrgb_prev = (0, 0, 0, 0, 0)
|
||||||
|
self.intensity = 65280
|
||||||
|
self.intred = 100
|
||||||
|
self.intgreen = 100
|
||||||
|
self.intblue = 100
|
||||||
|
self.newstream = self.OnePoint()
|
||||||
|
self.prev_x = 0
|
||||||
|
self.prev_y = 0
|
||||||
|
|
||||||
|
if gstt.debug > 0:
|
||||||
|
print("Tracer", self.mylaser, "init asked for ckey", self.clientkey + str(self.mylaser))
|
||||||
|
if self.connstatus != 0:
|
||||||
|
# print(""
|
||||||
|
log.err("Connection ERROR " + str(self.connstatus) + " with laser " + str(mylaser) + " : " + str(
|
||||||
|
gstt.lasersIPS[mylaser]))
|
||||||
|
# print("first 10 points in PL",self.PL, self.GetPoints(10)
|
||||||
|
else:
|
||||||
|
print("Connection status for DAC " + str(self.mylaser) + " : " + str(self.connstatus))
|
||||||
|
|
||||||
|
# Reference points
|
||||||
|
# Read the "hello" message
|
||||||
|
first_status = self.readresp("?")
|
||||||
|
first_status.dump()
|
||||||
|
position = []
|
||||||
|
|
||||||
|
def before_loop(self):
|
||||||
|
|
||||||
|
# print("laser", self.laser_id, "Pb : ",self.last_status.playback_state)
|
||||||
|
# error if DAC is already playing state (from other source)
|
||||||
|
if self.last_status.playback_state == 2:
|
||||||
|
raise Exception("already playing?!")
|
||||||
|
|
||||||
|
# if idle go to prepare state
|
||||||
|
elif self.last_status.playback_state == 0:
|
||||||
|
self.prepare()
|
||||||
|
|
||||||
|
def get_points_capacity(self):
|
||||||
|
cap = 1799 - self.last_status.fullness
|
||||||
|
if cap < 100:
|
||||||
|
time.sleep(0.001)
|
||||||
|
cap += 150
|
||||||
|
return
|
||||||
|
|
||||||
|
def read(self, l):
|
||||||
|
"""Read exactly length bytes from the connection."""
|
||||||
|
while l > len(self.buf):
|
||||||
|
self.buf += self.conn.recv(4096)
|
||||||
|
|
||||||
|
obuf = self.buf
|
||||||
|
self.buf = obuf[l:]
|
||||||
|
return obuf[:l]
|
||||||
|
|
||||||
|
def readresp(self, cmd):
|
||||||
|
"""Read a response from the DAC."""
|
||||||
|
|
||||||
|
data = self.read(22)
|
||||||
|
response = data[0]
|
||||||
|
gstt.lstt_dacanswers[self.mylaser] = response
|
||||||
|
cmdR = chr(data[1])
|
||||||
|
status = Status(data[2:])
|
||||||
|
|
||||||
|
self.redis.set('/lack/' + str(self.mylaser), response)
|
||||||
|
|
||||||
|
if cmdR != cmd:
|
||||||
|
raise ProtocolError("expected resp for %r, got %r"
|
||||||
|
% (cmd, cmdR))
|
||||||
|
|
||||||
|
if response != ord('a'):
|
||||||
|
raise ProtocolError("expected ACK, got %r"
|
||||||
|
% (response,))
|
||||||
|
|
||||||
|
self.last_status = status
|
||||||
|
return status
|
||||||
|
|
||||||
|
def begin(self, lwm, rate):
|
||||||
|
cmd = struct.pack("<cHI", b'b', lwm, rate)
|
||||||
|
print("Tracer", str(self.mylaser), "begin with PL : ", str(self.PL))
|
||||||
|
self.conn.sendall(cmd)
|
||||||
|
return self.readresp("b")
|
||||||
|
|
||||||
|
def update(self, lwm, rate):
|
||||||
|
print(("update", lwm, rate))
|
||||||
|
cmd = struct.pack("<cHI", b'u', lwm, rate)
|
||||||
|
self.conn.sendall(cmd)
|
||||||
|
return self.readresp("u")
|
||||||
|
|
||||||
|
def encode_point(self, point):
|
||||||
|
return pack_point(self.mylaser, self.intensity, *point)
|
||||||
|
|
||||||
|
def get_capacity(self):
|
||||||
|
""" How much points can we send next / are free in etherdream's buffer?"""
|
||||||
|
cap = 1799 - self.last_status.fullness
|
||||||
|
return cap
|
||||||
|
|
||||||
|
def write(self, points):
|
||||||
|
epoints = list(map(self.encode_point, points))
|
||||||
|
cmd = struct.pack("<cH", b'd', len(epoints))
|
||||||
|
self.conn.sendall(cmd + b''.join(epoints))
|
||||||
|
return self.readresp('d')
|
||||||
|
|
||||||
|
def get_warped_point(self, x, y ):
|
||||||
|
# Etherpoint all transform in one matrix, with warp !!
|
||||||
|
# xy : x y
|
||||||
|
|
||||||
|
# gstt.EDH[self.mylaser]= np.array(ast.literal_eval(self.redis.get('/EDH/'+str(self.mylaser))))
|
||||||
|
position = homographyp.apply(gstt.EDH[self.laser_id], np.array([(x, y)]))
|
||||||
|
|
||||||
|
# print("etherdream point",position[0][0], position[0][1], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
|
||||||
|
|
||||||
|
return position[0][0], position[0][1]
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
self.conn.sendall(b'p')
|
||||||
|
return self.readresp('p')
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.conn.sendall('s')
|
||||||
|
return self.readresp('s')
|
||||||
|
|
||||||
|
def estop(self):
|
||||||
|
self.conn.sendall("\xFF")
|
||||||
|
return self.readresp("\xFF")
|
||||||
|
|
||||||
|
def clear_estop(self):
|
||||||
|
self.conn.sendall("c")
|
||||||
|
return self.readresp("c")
|
||||||
|
|
||||||
|
def ping(self):
|
||||||
|
self.conn.sendall('?')
|
||||||
|
return self.readresp('?')
|
||||||
|
|
||||||
|
def play_stream(self):
|
||||||
|
|
||||||
|
# print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
|
||||||
|
|
||||||
|
# error if etherdream is already playing state (from other source)
|
||||||
|
if self.last_status.playback_state == 2:
|
||||||
|
raise Exception("already playing?!")
|
||||||
|
|
||||||
|
# if idle go to prepare state
|
||||||
|
elif self.last_status.playback_state == 0:
|
||||||
|
self.prepare()
|
||||||
|
|
||||||
|
started = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
|
||||||
|
# print("laser", self.mylaser, "Pb : ",self.last_status.playback_state)
|
||||||
|
|
||||||
|
order = int(self.redis.get('/order/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
# print("tracer", str(self.mylaser),"order", order, type(order)
|
||||||
|
|
||||||
|
if order == 0:
|
||||||
|
|
||||||
|
# USER point list
|
||||||
|
|
||||||
|
# self.pl = ast.literal_eval(self.redis.get(self.clientkey+str(self.mylaser)).decode('ascii'))
|
||||||
|
# print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
|
||||||
|
|
||||||
|
# si la clef est vide utiliser les points noirs ? -> syntax error -> black points.
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
# newpl = ""
|
||||||
|
# newpl = self.redis.get(self.clientkey+str(self.mylaser))
|
||||||
|
# self.pl = ast.literal_eval(newpl.decode('ascii'))
|
||||||
|
self.pl = ast.literal_eval(self.redis.get(self.clientkey + str(self.mylaser)).decode('ascii'))
|
||||||
|
|
||||||
|
except SyntaxError:
|
||||||
|
print("BAD POINTLIST on Tracer : laser", self.mylaser, " order 0 : pl : ", self.pl)
|
||||||
|
self.pl = black_points
|
||||||
|
|
||||||
|
# print("Tracer : laser", self.mylaser, " order 0 : pl : ",len(self.pl))
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# Get the new EDH
|
||||||
|
if order == 1:
|
||||||
|
print("Tracer", self.mylaser, "new EDH ORDER in redis")
|
||||||
|
gstt.EDH[self.mylaser] = np.array(
|
||||||
|
ast.literal_eval(self.redis.get('/EDH/' + str(self.mylaser)).decode('ascii')))
|
||||||
|
# Back to user point list
|
||||||
|
self.redis.set('/order/' + str(self.mylaser), 0)
|
||||||
|
|
||||||
|
# BLACK point list
|
||||||
|
if order == 2:
|
||||||
|
print("Tracer", self.mylaser, "BLACK ORDER in redis")
|
||||||
|
self.pl = black_points
|
||||||
|
|
||||||
|
# GRID point list
|
||||||
|
if order == 3:
|
||||||
|
print("Tracer", self.mylaser, "GRID ORDER in redis")
|
||||||
|
self.pl = grid_points
|
||||||
|
|
||||||
|
# Resampler Change
|
||||||
|
if order == 4:
|
||||||
|
self.resampler = ast.literal_eval(self.redis.get('/resampler/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
print("Tracer", self.mylaser, " : 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.mylaser), 0)
|
||||||
|
|
||||||
|
# Client Key change
|
||||||
|
if order == 5:
|
||||||
|
print("Tracer", self.mylaser, "new clientkey")
|
||||||
|
self.clientkey = self.redis.get('/clientkey')
|
||||||
|
# Back to user point list order
|
||||||
|
self.redis.set('/order/' + str(self.mylaser), 0)
|
||||||
|
|
||||||
|
# Intensity change
|
||||||
|
if order == 6:
|
||||||
|
self.intensity = int(self.redis.get('/intensity/' + str(self.mylaser)).decode('ascii')) << 8
|
||||||
|
print("Tracer", self.mylaser, "new Intensity", self.intensity)
|
||||||
|
gstt.intensity[self.mylaser] = self.intensity
|
||||||
|
self.redis.set('/order/' + str(self.mylaser), "0")
|
||||||
|
|
||||||
|
# kpps change
|
||||||
|
if order == 7:
|
||||||
|
gstt.kpps[self.mylaser] = int(self.redis.get('/kpps/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
print("Tracer", self.mylaser, "new kpps", gstt.kpps[self.mylaser])
|
||||||
|
self.update(0, gstt.kpps[self.mylaser])
|
||||||
|
self.redis.set('/order/' + str(self.mylaser), "0")
|
||||||
|
|
||||||
|
# color balance change
|
||||||
|
if order == 8:
|
||||||
|
self.intred = int(self.redis.get('/red/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
self.intgreen = int(self.redis.get('/green/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
self.intblue = int(self.redis.get('/blue/' + str(self.mylaser)).decode('ascii'))
|
||||||
|
print("Tracer", self.mylaser, "new color balance", self.intred, "% ", self.intgreen, "% ",
|
||||||
|
self.intblue, "% ")
|
||||||
|
self.redis.set('/order/' + str(self.mylaser), "0")
|
||||||
|
|
||||||
|
self.redis.set('/lstt/' + str(self.mylaser), self.last_status.playback_state)
|
||||||
|
# pdb.set_trace()
|
||||||
|
# How much room?
|
||||||
|
|
||||||
|
cap = 1799 - self.last_status.fullness
|
||||||
|
points = self.GetPoints(cap)
|
||||||
|
|
||||||
|
self.redis.set('/cap/' + str(self.mylaser), cap)
|
||||||
|
|
||||||
|
if cap < 100:
|
||||||
|
time.sleep(0.001)
|
||||||
|
cap += 150
|
||||||
|
|
||||||
|
# print("Writing %d points" % (cap, ))
|
||||||
|
# t0 = time.time()
|
||||||
|
# if self.mylaser == 2:
|
||||||
|
# print(points)
|
||||||
|
self.write(points)
|
||||||
|
# t1 = time.time()
|
||||||
|
# print("Took %f" % (t1 - t0, )
|
||||||
|
|
||||||
|
if not started:
|
||||||
|
print("Tracer", self.mylaser, "starting with", gstt.kpps[self.mylaser], "kpps")
|
||||||
|
self.begin(0, gstt.kpps[self.mylaser])
|
||||||
|
started = 1
|
121
libs3/tracer_helios.py
Normal file
121
libs3/tracer_helios.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
import ctypes
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
from libs3 import gstt
|
||||||
|
from libs3 import homographyp
|
||||||
|
from .tracer_common import Tracer, OnePointIterator, ProtocolError, Status
|
||||||
|
import numpy as np
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
# Define point structure
|
||||||
|
class HeliosPoint(ctypes.Structure):
|
||||||
|
# _pack_=1
|
||||||
|
_fields_ = [('x', ctypes.c_uint16),
|
||||||
|
('y', ctypes.c_uint16),
|
||||||
|
('r', ctypes.c_uint8),
|
||||||
|
('g', ctypes.c_uint8),
|
||||||
|
('b', ctypes.c_uint8),
|
||||||
|
('i', ctypes.c_uint8)]
|
||||||
|
|
||||||
|
|
||||||
|
# Load and initialize library
|
||||||
|
so_path = Path(__file__).absolute().parent.joinpath("libHeliosDacAPI.so")
|
||||||
|
HeliosLib = ctypes.cdll.LoadLibrary(so_path)
|
||||||
|
numDevices = HeliosLib.OpenDevices()
|
||||||
|
print("Found ", numDevices, "Helios DACs")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TracerHelios(Tracer):
|
||||||
|
"""A connection to a DAC."""
|
||||||
|
|
||||||
|
def __init__(self, laser_id, PL, redis):
|
||||||
|
self.redis = redis
|
||||||
|
self.laser_id = laser_id
|
||||||
|
self.PL = PL
|
||||||
|
self.pl = [[0, 0, 0]]
|
||||||
|
self.clientkey = self.redis.get("/clientkey").decode('ascii')
|
||||||
|
self.xyrgb = self.xyrgb_prev = (0, 0, 0, 0, 0)
|
||||||
|
self.intensity = 65280
|
||||||
|
self.intred = 100
|
||||||
|
self.intgreen = 100
|
||||||
|
self.intblue = 100
|
||||||
|
self.prev_x = 0
|
||||||
|
self.prev_y = 0
|
||||||
|
|
||||||
|
self.min_res = 0
|
||||||
|
self.max_res = 4095
|
||||||
|
|
||||||
|
# self.newstream = OnePointIterator()
|
||||||
|
|
||||||
|
# "Laser point List" Point generator
|
||||||
|
# each points is yielded : Getpoints() call n times OnePoint()
|
||||||
|
pass
|
||||||
|
|
||||||
|
def clip(self, number):
|
||||||
|
return int( self.min_res if number < self.min_res else self.max_res if number > self.max_res else number)
|
||||||
|
|
||||||
|
|
||||||
|
def get_points_capacity(self):
|
||||||
|
return 1000
|
||||||
|
|
||||||
|
# def GetPoints(self, capacity):
|
||||||
|
# a = [2,3]
|
||||||
|
# return a
|
||||||
|
|
||||||
|
def prepare(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def begin(self, n, kpps):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_status(self):
|
||||||
|
""" Return 0 if not ready (playing), 1 if ready to receive new frame,-1 if communication failed """
|
||||||
|
# va chercher dans le helios et renvoie la normalisée
|
||||||
|
status = HeliosLib.GetStatus(0)
|
||||||
|
if status == 0:
|
||||||
|
return self.lstate["2"] # playing
|
||||||
|
if status == 1:
|
||||||
|
return self.lstate["0"] # ready
|
||||||
|
if status == -1:
|
||||||
|
return self.lstate["64"] # no connection
|
||||||
|
|
||||||
|
def set_status(self, status: int):
|
||||||
|
return
|
||||||
|
|
||||||
|
def before_loop(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def write(self, points):
|
||||||
|
frame_type = HeliosPoint * self.get_points_capacity()
|
||||||
|
frame = frame_type()
|
||||||
|
helios_id = 0
|
||||||
|
points = [point for point in points]
|
||||||
|
for i, point in enumerate(points):
|
||||||
|
x, y, r, g, b = point
|
||||||
|
x *= 10
|
||||||
|
y *= 10
|
||||||
|
x = 0 if math.isnan(x) else self.clip(x)
|
||||||
|
y = 0 if math.isnan(y) else self.clip(y)
|
||||||
|
frame[i] = HeliosPoint(int(x), int(y), int(r), int(g), int(b), 255)
|
||||||
|
statusAttempts = 0
|
||||||
|
# Make 512 attempts for DAC status to be ready. After that, just give up and try to write the frame anyway
|
||||||
|
while (statusAttempts < 512 and HeliosLib.GetStatus(helios_id) != 1):
|
||||||
|
statusAttempts += 1
|
||||||
|
f = ctypes.pointer(frame)
|
||||||
|
# int HeliosDac::WriteFrame(unsigned int devNum, unsigned int pps, std::uint8_t flags, HeliosPoint* points, unsigned int numOfPoints)
|
||||||
|
# ret_helios = HeliosLib.WriteFrame(0, 3000, 0, f, len(frame))
|
||||||
|
# # @todo : detect errors
|
||||||
|
# if ret_helios != 1:
|
||||||
|
# print(f"ERR ]Helios DAC #{self.laser_id} returned error {ret_helios}")
|
||||||
|
|
||||||
|
def get_warped_point(self, x, y):
|
||||||
|
# transform in one matrix, with warp !!
|
||||||
|
# Etherpoint all transform in one matrix, with warp !!
|
||||||
|
np_arr = np.array([(x, y, 0)])
|
||||||
|
laser_edh = gstt.EDH[self.laser_id]
|
||||||
|
position = homographyp.apply(laser_edh, np_arr)
|
||||||
|
return position[0][0], position[0][1]
|
403
main.py
403
main.py
@ -22,9 +22,11 @@ All used ports:
|
|||||||
Plugins OSC Ports (see LJ.conf)
|
Plugins OSC Ports (see LJ.conf)
|
||||||
|
|
||||||
'''
|
'''
|
||||||
#import pdb
|
# import pdb
|
||||||
|
import traceback
|
||||||
|
|
||||||
from libs3 import log
|
from libs3 import log
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
print("")
|
print("")
|
||||||
log.infog("LJ Laser Server")
|
log.infog("LJ Laser Server")
|
||||||
@ -35,11 +37,13 @@ print("")
|
|||||||
|
|
||||||
import redis
|
import redis
|
||||||
import os
|
import os
|
||||||
ljpath = r'%s' % os.getcwd().replace('\\','/')
|
|
||||||
|
ljpath = r'%s' % os.getcwd().replace('\\', '/')
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
#sys.path.append('libs3/')
|
# sys.path.append('libs3/')
|
||||||
from libs3 import gstt, settings
|
from libs3 import gstt, settings
|
||||||
|
config = settings.config
|
||||||
gstt.ljpath = ljpath
|
gstt.ljpath = ljpath
|
||||||
|
|
||||||
log.info("Reading " + gstt.ConfigName + " setup file...")
|
log.info("Reading " + gstt.ConfigName + " setup file...")
|
||||||
@ -47,68 +51,64 @@ settings.Read()
|
|||||||
|
|
||||||
# Arguments may alter .conf file so import settings first then cli
|
# Arguments may alter .conf file so import settings first then cli
|
||||||
from libs3 import cli
|
from libs3 import cli
|
||||||
|
|
||||||
settings.Write()
|
settings.Write()
|
||||||
|
|
||||||
from multiprocessing import Process, set_start_method
|
from multiprocessing import Process, set_start_method
|
||||||
import random, ast
|
import random, ast
|
||||||
from libs3 import plugins
|
from libs3 import plugins
|
||||||
|
|
||||||
|
# from libs3 import lasytracer as tracer
|
||||||
#from libs3 import lasytracer as tracer
|
from libs3 import tracer
|
||||||
from libs3 import tracer3 as tracer
|
|
||||||
|
|
||||||
|
|
||||||
from libs3 import homographyp, commands, font1
|
from libs3 import homographyp, commands, font1
|
||||||
#import subprocess
|
# import subprocess
|
||||||
import os
|
import os
|
||||||
#import midi
|
# import midi
|
||||||
from libs3 import OSC3
|
from libs3 import OSC3
|
||||||
from websocket_server import WebsocketServer
|
from websocket_server import WebsocketServer
|
||||||
#import socket
|
# import socket
|
||||||
import types, _thread, time
|
import types, _thread, time
|
||||||
|
|
||||||
|
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
|
||||||
|
|
||||||
r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0)
|
|
||||||
# r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
|
# r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
|
||||||
args =[0,0]
|
args = [0, 0]
|
||||||
|
|
||||||
|
|
||||||
def dac_process(number, pl):
|
def dac_process(number, pl, dac_family):
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from libs3 import gstt
|
from libs3 import gstt
|
||||||
|
|
||||||
print("Starting dac process", number)
|
print("Starting dac process", number)
|
||||||
|
try:
|
||||||
|
d = tracer.DAC(number, pl, dac_family=dac_family)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
|
||||||
d = tracer.DAC(number,pl)
|
|
||||||
d.play_stream()
|
d.play_stream()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
if gstt.debug > 0:
|
if gstt.debug > 0:
|
||||||
log.err('\n---------------------')
|
# if True:
|
||||||
log.err('Exception: %s' % e)
|
log.err('\n---------------------')
|
||||||
log.err('- - - - - - - - - - -')
|
log.err('Exception: %s' % e)
|
||||||
traceback.print_tb(sys.exc_info()[2])
|
log.err('- - - - - - - - - - -')
|
||||||
print("\n")
|
traceback.print_tb(sys.exc_info()[2])
|
||||||
pass
|
print("\n")
|
||||||
|
pass
|
||||||
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Servers init variables
|
# Servers init variables
|
||||||
#
|
#
|
||||||
|
|
||||||
print("Start Scene number :",gstt.SceneNumber)
|
print("Start Scene number :", gstt.SceneNumber)
|
||||||
|
|
||||||
print("WebUI connect to :", gstt.wwwIP)
|
print("WebUI connect to :", gstt.wwwIP)
|
||||||
|
|
||||||
@ -121,26 +121,25 @@ print("OSCserver IP :", oscserverIP)
|
|||||||
nozoscIP = gstt.nozoscip
|
nozoscIP = gstt.nozoscip
|
||||||
print("Nozosc IP :", nozoscIP)
|
print("Nozosc IP :", nozoscIP)
|
||||||
|
|
||||||
debug = gstt.debug
|
# gstt.debug = 1
|
||||||
|
debug = gstt.debug
|
||||||
print("Debug :", debug)
|
print("Debug :", debug)
|
||||||
|
|
||||||
|
|
||||||
# Websocket listening port
|
# Websocket listening port
|
||||||
wsPORT = 9001
|
wsPORT = 9001
|
||||||
|
|
||||||
# oscserver
|
# oscserver
|
||||||
# OSC Server : accept OSC message on port 8002
|
# OSC Server : accept OSC message on port 8002
|
||||||
#oscIPin = "192.168.1.10"s
|
# oscIPin = "192.168.1.10"s
|
||||||
oscserverIPin = serverIP
|
oscserverIPin = serverIP
|
||||||
|
|
||||||
print("oscserverIPin", oscserverIPin)
|
print("oscserverIPin", oscserverIPin)
|
||||||
oscserverPORTin = 8002
|
oscserverPORTin = 8002
|
||||||
|
|
||||||
# OSC Client : to send OSC message to an IP port 8001
|
# OSC Client : to send OSC message to an IP port 8001
|
||||||
oscserverIPout = oscserverIP
|
oscserverIPout = oscserverIP
|
||||||
oscserverPORTout = 8001
|
oscserverPORTout = 8001
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
# Nozoid OSC Client : to send OSC message to Nozoid inport 8003
|
# Nozoid OSC Client : to send OSC message to Nozoid inport 8003
|
||||||
NozoscIPout = nozoscIP
|
NozoscIPout = nozoscIP
|
||||||
@ -155,15 +154,15 @@ planetPORTout = plugins.Port("planet")
|
|||||||
|
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
#retry = 1
|
# retry = 1
|
||||||
#delay = 1
|
# delay = 1
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# OSC
|
# OSC
|
||||||
#
|
#
|
||||||
|
|
||||||
oscserver = OSC3.OSCServer( (oscserverIPin, oscserverPORTin) )
|
oscserver = OSC3.OSCServer((oscserverIPin, oscserverPORTin))
|
||||||
oscserver.timeout = 0
|
oscserver.timeout = 0
|
||||||
OSCRunning = True
|
OSCRunning = True
|
||||||
|
|
||||||
@ -171,110 +170,112 @@ OSCRunning = True
|
|||||||
def handle_timeout(self):
|
def handle_timeout(self):
|
||||||
self.timed_out = True
|
self.timed_out = True
|
||||||
|
|
||||||
|
|
||||||
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
|
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
|
||||||
|
|
||||||
|
|
||||||
# OSC default path handler : send incoming OSC message to UI via websocket 9001
|
# OSC default path handler : send incoming OSC message to UI via websocket 9001
|
||||||
def handler(path, tags, args, source):
|
def handler(path, tags, args, source):
|
||||||
|
|
||||||
oscpath = path.split("/")
|
oscpath = path.split("/")
|
||||||
if gstt.debug > 0:
|
if gstt.debug > 0:
|
||||||
print("")
|
print("")
|
||||||
print("OSC handler in main said : path", path," oscpath ", oscpath," args", args)
|
print("OSC handler in main said : path", path, " oscpath ", oscpath, " args", args)
|
||||||
|
|
||||||
if oscpath[1] != "pong":
|
if oscpath[1] != "pong":
|
||||||
sendWSall(path + " " + str(args[0]))
|
sendWSall(path + " " + str(args[0]))
|
||||||
commands.handler(oscpath,args)
|
commands.handler(oscpath, args)
|
||||||
|
|
||||||
|
|
||||||
# RAW OSC Frame available ?
|
# RAW OSC Frame available ?
|
||||||
def osc_frame():
|
def osc_frame():
|
||||||
#print 'oscframe'
|
# print 'oscframe'
|
||||||
# clear timed_out flag
|
# clear timed_out flag
|
||||||
oscserver.timed_out = False
|
oscserver.timed_out = False
|
||||||
# handle all pending requests then return
|
# handle all pending requests then return
|
||||||
while not oscserver.timed_out:
|
while not oscserver.timed_out:
|
||||||
oscserver.handle_request()
|
oscserver.handle_request()
|
||||||
|
|
||||||
def PingAll():
|
|
||||||
|
|
||||||
|
def PingAll():
|
||||||
if gstt.debug > 0:
|
if gstt.debug > 0:
|
||||||
print("Pinging all plugins...")
|
print("Pinging all plugins...")
|
||||||
|
|
||||||
for plugin in list(gstt.plugins.keys()):
|
for plugin in list(gstt.plugins.keys()):
|
||||||
if gstt.debug > 0:
|
if gstt.debug > 0:
|
||||||
print("pinging", plugin)
|
print("pinging", plugin)
|
||||||
#sendWSall("/"+ plugin + "/start 0")
|
# sendWSall("/"+ plugin + "/start 0")
|
||||||
plugins.Ping(plugin)
|
plugins.Ping(plugin)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# OSC server Thread : handler, dacs reports and simulator points sender to UI.
|
# OSC server Thread : handler, dacs reports and simulator points sender to UI.
|
||||||
def osc_thread():
|
def osc_thread():
|
||||||
|
# while True:
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
|
||||||
#while True:
|
time.sleep(0.1)
|
||||||
try:
|
osc_frame()
|
||||||
while True:
|
for laserid in range(0, gstt.LaserNumber): # Laser not used -> led is not lit
|
||||||
|
|
||||||
time.sleep(0.1)
|
lstate = {'0': 'IDLE', '1': 'PREPARE', '2': "PLAYING", '64': "NOCONNECTION ?"}
|
||||||
osc_frame()
|
lstt = r.get('/lstt/' + str(laserid)).decode('ascii')
|
||||||
for laserid in range(0,gstt.LaserNumber): # Laser not used -> led is not lit
|
# print ("laserid", laserid,"lstt",lstt, type(lstt))
|
||||||
|
if gstt.debug > 1:
|
||||||
|
print("DAC", laserid, "is in (lstt) :", lstt, lstate[str(lstt)])
|
||||||
|
if lstt == "0": # Dac IDLE state(0) -> led is blue (3)
|
||||||
|
sendWSall("/lstt/" + str(laserid) + " 3")
|
||||||
|
|
||||||
lstate = {'0': 'IDLE', '1': 'PREPARE', '2': "PLAYING", '64': "NOCONNECTION ?" }
|
if lstt == "1": # Dac PREPARE state (1) -> led is cyan (2)
|
||||||
lstt = r.get('/lstt/'+ str(laserid)).decode('ascii')
|
sendWSall("/lstt/" + str(laserid) + " 2")
|
||||||
#print ("laserid", laserid,"lstt",lstt, type(lstt))
|
|
||||||
if gstt.debug >1:
|
|
||||||
print("DAC", laserid, "is in (lstt) :", lstt , lstate[str(lstt)])
|
|
||||||
if lstt == "0": # Dac IDLE state(0) -> led is blue (3)
|
|
||||||
sendWSall("/lstt/" + str(laserid) + " 3")
|
|
||||||
|
|
||||||
if lstt == "1": # Dac PREPARE state (1) -> led is cyan (2)
|
if lstt == "2": # Dac PLAYING (2) -> led is green (1)
|
||||||
sendWSall("/lstt/" + str(laserid) + " 2")
|
sendWSall("/lstt/" + str(laserid) + " 1")
|
||||||
|
|
||||||
if lstt == "2": # Dac PLAYING (2) -> led is green (1)
|
ackstate = {'61': 'ACK', '46': 'FULL', '49': "INVALID", '21': 'STOP', '64': "NOCONNECTION ?",
|
||||||
sendWSall("/lstt/" + str(laserid) + " 1")
|
'35': "NOCONNECTION ?", '97': 'ACK', '70': 'FULL', '73': "INVALID", '33': 'STOP',
|
||||||
|
'100': "NOCONNECTION", '48': "NOCONNECTION", 'a': 'ACK', 'F': 'FULL', 'I': "INVALID",
|
||||||
|
'!': 'STOP', 'd': "NOCONNECTION", '0': "NOCONNECTION"}
|
||||||
|
lack = r.get('/lack/' + str(laserid)).decode('ascii')
|
||||||
|
|
||||||
ackstate = {'61': 'ACK', '46': 'FULL', '49': "INVALID", '21': 'STOP', '64': "NOCONNECTION ?", '35': "NOCONNECTION ?" , '97': 'ACK', '70': 'FULL', '73': "INVALID", '33': 'STOP', '100': "NOCONNECTION", '48': "NOCONNECTION", 'a': 'ACK', 'F': 'FULL', 'I': "INVALID", '!': 'STOP', 'd': "NOCONNECTION", '0': "NOCONNECTION"}
|
if gstt.debug > 1:
|
||||||
lack= r.get('/lack/'+str(laserid)).decode('ascii')
|
print("DAC", laserid, "answered (lack):", lack, chr(int(lack)), ackstate[str(lack)])
|
||||||
|
|
||||||
if gstt.debug >1:
|
if chr(int(lack)) == 'a': # Dac sent ACK ("a") -> led is green (1)
|
||||||
print("DAC", laserid, "answered (lack):", lack, chr(int(lack)), ackstate[str(lack)])
|
sendWSall("/lack/" + str(laserid) + " 1")
|
||||||
|
|
||||||
if chr(int(lack)) == 'a': # Dac sent ACK ("a") -> led is green (1)
|
if chr(int(lack)) == 'F': # Dac sent FULL ("F") -> led is orange (5)
|
||||||
sendWSall("/lack/" + str(laserid) +" 1")
|
sendWSall("/lack/" + str(laserid) + " 5")
|
||||||
|
|
||||||
if chr(int(lack)) == 'F': # Dac sent FULL ("F") -> led is orange (5)
|
if chr(int(lack)) == 'I': # Dac sent INVALID ("I") -> led is yellow (4)
|
||||||
sendWSall("/lack/" + str(laserid) +" 5")
|
sendWSall("/lack/" + str(laserid) + " 4")
|
||||||
|
# print lack
|
||||||
|
|
||||||
if chr(int(lack)) == 'I': # Dac sent INVALID ("I") -> led is yellow (4)
|
if lack == "64" or lack == "35": # no connection to dac -> leds are red (6)
|
||||||
sendWSall("/lack/" + str(laserid)+" 4")
|
sendWSall("/lack/" + str(laserid) + " 6")
|
||||||
#print lack
|
sendWSall("/lstt/" + str(laserid) + " 6")
|
||||||
|
# sendWSall("/lstt/" + str(laserid) + " 0")
|
||||||
if lack == "64" or lack =="35": # no connection to dac -> leds are red (6)
|
sendWSall("/points/" + str(laserid) + " 6")
|
||||||
sendWSall("/lack/" + str(laserid) + " 6")
|
|
||||||
sendWSall("/lstt/" + str(laserid) + " 6")
|
|
||||||
#sendWSall("/lstt/" + str(laserid) + " 0")
|
|
||||||
sendWSall("/points/" + str(laserid) + " 6")
|
|
||||||
|
|
||||||
else:
|
|
||||||
# last number of points sent to etherdream buffer
|
|
||||||
sendWSall("/points/" + str(laserid) + " " + str(r.get('/cap/'+str(laserid)).decode('ascii')))
|
|
||||||
|
|
||||||
#print("Sending simu frame from",'/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser))
|
else:
|
||||||
#print(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)))
|
# last number of points sent to etherdream buffer
|
||||||
sendWSall("/simul" +" "+ str(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)).decode('ascii')))
|
sendWSall("/points/" + str(laserid) + " " + str(r.get('/cap/' + str(laserid)).decode('ascii')))
|
||||||
if random.randint(0,100)>95:
|
|
||||||
plugins.sendbroadcast()
|
# print("Sending simu frame from",'/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser))
|
||||||
|
# print(r.get('/pl/'+str(gstt.SceneNumber)+'/'+str(gstt.Laser)))
|
||||||
|
sendWSall(
|
||||||
|
"/simul" + " " + str(r.get('/pl/' + str(gstt.SceneNumber) + '/' + str(gstt.Laser)).decode('ascii')))
|
||||||
|
if random.randint(0, 100) > 95:
|
||||||
|
plugins.sendbroadcast()
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
import sys, traceback
|
import sys, traceback
|
||||||
print('\n--------------------------')
|
print('\n--------------------------')
|
||||||
print('OSC Thread Exception: %s' % e)
|
print('OSC Thread Exception: %s' % e)
|
||||||
print('- - - - - - - - - - - - - - ')
|
print('- - - - - - - - - - - - - - ')
|
||||||
traceback.print_tb(sys.exc_info()[2])
|
traceback.print_tb(sys.exc_info()[2])
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Websocket part
|
# Websocket part
|
||||||
@ -282,33 +283,33 @@ def osc_thread():
|
|||||||
|
|
||||||
# Called for every WS client connecting (after handshake)
|
# Called for every WS client connecting (after handshake)
|
||||||
def new_client(client, wserver):
|
def new_client(client, wserver):
|
||||||
|
|
||||||
print("New WS client connected and was given id %d" % client['id'])
|
print("New WS client connected and was given id %d" % client['id'])
|
||||||
sendWSall("/status Hello " + str(client['id']))
|
sendWSall("/status Hello " + str(client['id']))
|
||||||
|
|
||||||
for laserid in range(0,gstt.LaserNumber):
|
for laserid in range(0, gstt.LaserNumber):
|
||||||
|
|
||||||
sendWSall("/ip/" + str(laserid) + " " + str(gstt.lasersIPS[laserid]))
|
sendWSall("/ip/" + str(laserid) + " " + str(gstt.lasersIPS[laserid]))
|
||||||
sendWSall("/kpps/" + str(laserid)+ " " + str(gstt.kpps[laserid]))
|
sendWSall("/kpps/" + str(laserid) + " " + str(gstt.kpps[laserid]))
|
||||||
#sendWSall("/laser"+str(laserid)+"/start 1")
|
# sendWSall("/laser"+str(laserid)+"/start 1")
|
||||||
sendWSall("/laser "+str(laserid))
|
sendWSall("/laser " + str(laserid))
|
||||||
#print("/laser "+str(laserid))
|
# print("/laser "+str(laserid))
|
||||||
sendWSall("/lack/" + str(laserid) + " 6")
|
sendWSall("/lack/" + str(laserid) + " 6")
|
||||||
#print("/lack/" + str(laserid) + " 6")
|
# print("/lack/" + str(laserid) + " 6")
|
||||||
sendWSall("/lstt/" + str(laserid) + " 6")
|
sendWSall("/lstt/" + str(laserid) + " 6")
|
||||||
#print("/lstt/" + str(laserid) + " 6")
|
# print("/lstt/" + str(laserid) + " 6")
|
||||||
sendWSall("/points/" + str(laserid) + " 0")
|
sendWSall("/points/" + str(laserid) + " 0")
|
||||||
#print("/points/" + str(laserid) + " 0")
|
# print("/points/" + str(laserid) + " 0")
|
||||||
|
|
||||||
if gstt.swapX[laserid] == 1:
|
if gstt.swapX[laserid] == 1:
|
||||||
sendWSall("/swap/X/" + str(laserid)+ " 1")
|
sendWSall("/swap/X/" + str(laserid) + " 1")
|
||||||
else:
|
else:
|
||||||
sendWSall("/swap/X/" + str(laserid)+ " 0")
|
sendWSall("/swap/X/" + str(laserid) + " 0")
|
||||||
|
|
||||||
if gstt.swapY[laserid] == 1:
|
if gstt.swapY[laserid] == 1:
|
||||||
sendWSall("/swap/Y/" + str(laserid)+ " 1")
|
sendWSall("/swap/Y/" + str(laserid) + " 1")
|
||||||
else:
|
else:
|
||||||
sendWSall("/swap/Y/" + str(laserid)+ " 0")
|
sendWSall("/swap/Y/" + str(laserid) + " 0")
|
||||||
|
|
||||||
|
|
||||||
# Called for every WS client disconnecting
|
# Called for every WS client disconnecting
|
||||||
def client_left(client, wserver):
|
def client_left(client, wserver):
|
||||||
@ -317,53 +318,50 @@ def client_left(client, wserver):
|
|||||||
|
|
||||||
# Called for each WS received message.
|
# Called for each WS received message.
|
||||||
def message_received(client, wserver, message):
|
def message_received(client, wserver, message):
|
||||||
|
# if len(message) > 200:
|
||||||
#if len(message) > 200:
|
|
||||||
# message = message[:200]+'..'
|
# message = message[:200]+'..'
|
||||||
|
|
||||||
#if gstt.debug >0:
|
# if gstt.debug >0:
|
||||||
# print ("")
|
# print ("")
|
||||||
# print("WS Client(%d) said: %s" % (client['id'], message))
|
# print("WS Client(%d) said: %s" % (client['id'], message))
|
||||||
|
|
||||||
oscpath = message.split(" ")
|
oscpath = message.split(" ")
|
||||||
#print "WS Client", client['id'], "said :", message, "splitted in an oscpath :", oscpath
|
# print "WS Client", client['id'], "said :", message, "splitted in an oscpath :", oscpath
|
||||||
if gstt.debug > 0:
|
if gstt.debug > 0:
|
||||||
print("WS Client", client['id'], "said :", message, "splitted in an oscpath :", oscpath)
|
print("WS Client", client['id'], "said :", message, "splitted in an oscpath :", oscpath)
|
||||||
|
|
||||||
PingAll()
|
PingAll()
|
||||||
message4plugin = False
|
message4plugin = False
|
||||||
|
|
||||||
# WS received Message is for a plugin ?
|
# WS received Message is for a plugin ?
|
||||||
|
|
||||||
for plugin in list(gstt.plugins.keys()):
|
for plugin in list(gstt.plugins.keys()):
|
||||||
|
|
||||||
if oscpath[0].find(plugin) != -1:
|
if oscpath[0].find(plugin) != -1:
|
||||||
|
|
||||||
message4plugin = True
|
message4plugin = True
|
||||||
#print(oscpath)
|
# print(oscpath)
|
||||||
if plugins.Send(plugin, oscpath):
|
if plugins.Send(plugin, oscpath):
|
||||||
print("plugins sent incoming WS correctly to", plugin)
|
print("plugins sent incoming WS correctly to", plugin)
|
||||||
else:
|
else:
|
||||||
print("plugins detected", plugin, "offline.")
|
print("plugins detected", plugin, "offline.")
|
||||||
|
|
||||||
|
# WS received message is an LJ command
|
||||||
# WS received message is an LJ command
|
|
||||||
|
|
||||||
if message4plugin == False:
|
if message4plugin == False:
|
||||||
|
|
||||||
if len(oscpath) == 1:
|
if len(oscpath) == 1:
|
||||||
args[0] = "noargs"
|
args[0] = "noargs"
|
||||||
#print "noargs command"
|
# print "noargs command"
|
||||||
|
|
||||||
elif len(oscpath) > 1:
|
elif len(oscpath) > 1:
|
||||||
args[0] = str(oscpath[1])
|
args[0] = str(oscpath[1])
|
||||||
#print "arg",oscpath[1]
|
# print "arg",oscpath[1]
|
||||||
|
|
||||||
commands.handler(oscpath[0].split("/"),args)
|
commands.handler(oscpath[0].split("/"), args)
|
||||||
|
|
||||||
|
|
||||||
# if needed a loop back : WS Client -> server -> WS Client
|
# if needed a loop back : WS Client -> server -> WS Client
|
||||||
#sendWSall("ws"+message)
|
# sendWSall("ws"+message)
|
||||||
|
|
||||||
|
|
||||||
def handle_timeout(self):
|
def handle_timeout(self):
|
||||||
@ -371,10 +369,11 @@ def handle_timeout(self):
|
|||||||
|
|
||||||
|
|
||||||
def sendWSall(message):
|
def sendWSall(message):
|
||||||
#if gstt.debug >0:
|
# if gstt.debug >0:
|
||||||
#print("WS sending %s" % (message))
|
# print("WS sending %s" % (message))
|
||||||
wserver.send_message_to_all(message)
|
wserver.send_message_to_all(message)
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
print ""
|
print ""
|
||||||
print "Midi Configuration"
|
print "Midi Configuration"
|
||||||
@ -382,10 +381,11 @@ midi.InConfig()
|
|||||||
midi.OutConfig()
|
midi.OutConfig()
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def fff(name):
|
def fff(name):
|
||||||
print()
|
print()
|
||||||
print('HELLO', name ) #indent
|
print('HELLO', name) # indent
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -395,26 +395,24 @@ def fff(name):
|
|||||||
print("")
|
print("")
|
||||||
log.info("Creating startup point lists...")
|
log.info("Creating startup point lists...")
|
||||||
|
|
||||||
|
if r.set("/clientkey", "/pl/" + str(gstt.SceneNumber) + "/") == True:
|
||||||
|
print("sent clientkey : /pl/" + str(gstt.SceneNumber) + "/")
|
||||||
|
|
||||||
if r.set("/clientkey","/pl/"+str(gstt.SceneNumber)+"/")==True:
|
# pdb.set_trace()
|
||||||
print("sent clientkey : /pl/"+str(gstt.SceneNumber)+"/")
|
for sceneid in range(0, gstt.MaxScenes + 1):
|
||||||
|
print("Scene " + str(sceneid))
|
||||||
#pdb.set_trace()
|
# digit_points = font1.DigitsDots(sceneid,65280)
|
||||||
for sceneid in range(0,gstt.MaxScenes+1):
|
|
||||||
print("Scene "+ str(sceneid))
|
|
||||||
#digit_points = font1.DigitsDots(sceneid,65280)
|
|
||||||
|
|
||||||
# Order all lasers to show its number at startup -> tell all 4 laser process to USER PLs
|
# Order all lasers to show its number at startup -> tell all 4 laser process to USER PLs
|
||||||
for laserid in range(0,gstt.LaserNumber):
|
for laserid in range(0, gstt.LaserNumber):
|
||||||
|
|
||||||
digit_points = font1.DigitsDots(laserid,65280)
|
digit_points = font1.DigitsDots(laserid, 65280)
|
||||||
if r.set('/pl/'+str(sceneid)+'/'+str(laserid), str(digit_points)) == True:
|
if r.set('/pl/' + str(sceneid) + '/' + str(laserid), str(digit_points)) == True:
|
||||||
pass
|
pass
|
||||||
#print( ast.literal_eval(r.get('/pl/'+str(sceneid)+'/'+str(laserid)).decode('ascii')))
|
# print( ast.literal_eval(r.get('/pl/'+str(sceneid)+'/'+str(laserid)).decode('ascii')))
|
||||||
#print("/pl/"+str(sceneid)+"/"+str(laserid)+" "+str(ast.literal_eval(r.get('/pl/'+str(sceneid)+'/'+str(laserid)).decode('ascii'))))
|
# print("/pl/"+str(sceneid)+"/"+str(laserid)+" "+str(ast.literal_eval(r.get('/pl/'+str(sceneid)+'/'+str(laserid)).decode('ascii'))))
|
||||||
|
|
||||||
r.set('/order/'+str(laserid), 0)
|
|
||||||
|
|
||||||
|
r.set('/order/' + str(laserid), 0)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Starts one DAC process per requested Laser
|
# Starts one DAC process per requested Laser
|
||||||
@ -431,96 +429,98 @@ if __name__ == '__main__':
|
|||||||
commands.DAChecks()
|
commands.DAChecks()
|
||||||
print("dacs", gstt.dacs)
|
print("dacs", gstt.dacs)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
log.infog("Resquested DACs mode")
|
log.infog("Resquested DACs mode")
|
||||||
|
|
||||||
lasernumber = gstt.LaserNumber -1
|
lasernumber = gstt.LaserNumber - 1
|
||||||
print("LaserNumber = ", gstt.LaserNumber)
|
print("LaserNumber = ", gstt.LaserNumber)
|
||||||
|
|
||||||
|
log.info("Starting " + str(gstt.LaserNumber) + " DACs process...")
|
||||||
log.info("Starting "+str(gstt.LaserNumber) + " DACs process...")
|
|
||||||
|
# Launch one process (a tracer3 instance) for etherdream / helios
|
||||||
# Launch one process (a tracer3 instance) by etherdream
|
dac_family = None
|
||||||
dac_worker0= Process(target=dac_process, args=(0,0,))
|
if config["laser0"].get("dac_family"):
|
||||||
|
dac_family = config["laser0"]["dac_family"]
|
||||||
|
dac_worker0 = Process(target=dac_process, args=(0, 0, dac_family))
|
||||||
dac_worker0.start()
|
dac_worker0.start()
|
||||||
commands.worker0 = dac_worker0
|
commands.worker0 = dac_worker0
|
||||||
print("Tracer 0 : name", dac_worker0.name , "pid", dac_worker0.pid )
|
print("Tracer 0 : name", dac_worker0.name, "pid", dac_worker0.pid)
|
||||||
|
|
||||||
if lasernumber >0:
|
if lasernumber > 0:
|
||||||
dac_worker1= Process(target=dac_process, args=(1,0,))
|
dac_worker1 = Process(target=dac_process, args=(1, 0,))
|
||||||
commands.worker1 = dac_worker1
|
commands.worker1 = dac_worker1
|
||||||
print("Tracer 1 : name", dac_worker1.name , "pid", dac_worker1.pid )
|
print("Tracer 1 : name", dac_worker1.name, "pid", dac_worker1.pid)
|
||||||
dac_worker1.start()
|
dac_worker1.start()
|
||||||
|
|
||||||
if lasernumber >1:
|
if lasernumber > 1:
|
||||||
dac_worker2= Process(target=dac_process, args=(2,0,))
|
dac_worker2 = Process(target=dac_process, args=(2, 0,))
|
||||||
dac_worker2.start()
|
dac_worker2.start()
|
||||||
commands.worker2 = dac_worker2
|
commands.worker2 = dac_worker2
|
||||||
print("Tracer 2 : name", dac_worker2.name , "pid", dac_worker2.pid )
|
print("Tracer 2 : name", dac_worker2.name, "pid", dac_worker2.pid)
|
||||||
|
|
||||||
if lasernumber >2:
|
if lasernumber > 2:
|
||||||
dac_worker3= Process(target=dac_process, args=(3,0,))
|
dac_worker3 = Process(target=dac_process, args=(3, 0,))
|
||||||
print("Tracer 3 : name", dac_worker3.name , "pid", dac_worker3.pid )
|
print("Tracer 3 : name", dac_worker3.name, "pid", dac_worker3.pid)
|
||||||
commands.worker3 = dac_worker3
|
commands.worker3 = dac_worker3
|
||||||
dac_worker3.start()
|
dac_worker3.start()
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
#
|
#
|
||||||
# start WS and OSC servers
|
# start WS and OSC servers
|
||||||
#
|
#
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
||||||
# Websocket startup
|
# Websocket startup
|
||||||
wserver = WebsocketServer(wsPORT,host=serverIP)
|
wserver = WebsocketServer(wsPORT, host=serverIP)
|
||||||
plugins.Init(wserver)
|
plugins.Init(wserver)
|
||||||
|
|
||||||
log.info("Starting servers...")
|
log.info("Starting servers...")
|
||||||
# Launch OSC thread listening to oscserver
|
# Launch OSC thread listening to oscserver
|
||||||
print("Launching OSC server...")
|
# print("Launching OSC server...")
|
||||||
print("at", oscserverIPin, "port",str(oscserverPORTin))
|
# print("at", oscserverIPin, "port", str(oscserverPORTin))
|
||||||
oscserver.addMsgHandler( "/noteon", commands.NoteOn)
|
# oscserver.addMsgHandler("/noteon", commands.NoteOn)
|
||||||
oscserver.addMsgHandler( "/scim", commands.Scim)
|
# oscserver.addMsgHandler("/scim", commands.Scim)
|
||||||
oscserver.addMsgHandler( "/line1", commands.Line1)
|
# oscserver.addMsgHandler("/line1", commands.Line1)
|
||||||
oscserver.addMsgHandler( "/forwardui", commands.ForwardUI)
|
# oscserver.addMsgHandler("/forwardui", commands.ForwardUI)
|
||||||
# Default OSC handler for all OSC incoming message
|
# # Default OSC handler for all OSC incoming message
|
||||||
oscserver.addMsgHandler("default", handler)
|
# oscserver.addMsgHandler("default", handler)
|
||||||
_thread.start_new_thread(osc_thread, ())
|
# _thread.start_new_thread(osc_thread, ())
|
||||||
|
|
||||||
print("Launching webUI Websocket server...")
|
print("Launching webUI Websocket server...")
|
||||||
print("at", serverIP, "port",wsPORT)
|
print("at", serverIP, "port", wsPORT)
|
||||||
wserver.set_fn_new_client(new_client)
|
wserver.set_fn_new_client(new_client)
|
||||||
wserver.set_fn_client_left(client_left)
|
wserver.set_fn_client_left(client_left)
|
||||||
wserver.set_fn_message_received(message_received)
|
wserver.set_fn_message_received(message_received)
|
||||||
print("")
|
print("")
|
||||||
log.info("Resetting all Homographies...")
|
log.info("Resetting all Homographies...")
|
||||||
for laserid in range(0,gstt.LaserNumber):
|
for laserid in range(0, gstt.LaserNumber):
|
||||||
homographyp.newEDH(laserid)
|
homographyp.newEDH(laserid)
|
||||||
|
|
||||||
# plugins autostart
|
# plugins autostart
|
||||||
print("")
|
print("")
|
||||||
log.info("Plugins startup...")
|
log.info("Plugins startup...")
|
||||||
|
|
||||||
if gstt.autostart != "":
|
if gstt.autostart != "":
|
||||||
|
|
||||||
for pluginname in gstt.autostart.split(","):
|
for pluginname in gstt.autostart.split(","):
|
||||||
print("Autostarting", pluginname, "...")
|
print("Autostarting", pluginname, "...")
|
||||||
plugins.Start(pluginname)
|
plugins.Start(pluginname)
|
||||||
|
|
||||||
print("")
|
print("")
|
||||||
log.infog("LJ server running...")
|
log.infog("LJ server running...")
|
||||||
|
|
||||||
# websocket loop
|
# websocket loop
|
||||||
|
|
||||||
wserver.run_forever()
|
wserver.run_forever()
|
||||||
|
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
log.err("Exception")
|
log.err("Exception")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
# Gently stop on CTRL C
|
# Gently stop on CTRL C
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
|
|
||||||
commands.LJautokill()
|
commands.LJautokill()
|
||||||
@ -530,6 +530,3 @@ if __name__ == '__main__':
|
|||||||
|
|
||||||
random_points = [(300.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 0), (500.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 65280), (500.0+random.randint(-100, 100), 400.0+random.randint(-100, 100), 65280), (300.0+random.randint(-100, 100), 400.0+random.randint(-100, 100), 65280), (300.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 65280)]
|
random_points = [(300.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 0), (500.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 65280), (500.0+random.randint(-100, 100), 400.0+random.randint(-100, 100), 65280), (300.0+random.randint(-100, 100), 400.0+random.randint(-100, 100), 65280), (300.0+random.randint(-100, 100), 200.0+random.randint(-100, 100), 65280)]
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,48 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
sudo apt upgrade
|
|
||||||
sudo apt install python3-pip
|
# Check if root
|
||||||
sudo apt install redis-server
|
[[ 0 -ne $UID ]] && { echo "Must run as root. Exiting."; exit; }
|
||||||
# for dedicated computer after fresh linux install. todo : ask if needed.
|
|
||||||
#sudo apt install git
|
# Require a user for exploitation
|
||||||
#sudo apt install syncthing
|
DEFAULT=$(getent passwd 1000|cut -d: -f1)
|
||||||
#sudo apt install htop
|
read -e -i "$DEFAULT" -p "Please provide the system user who will own the project: " USER
|
||||||
#sudo apt install screen
|
|
||||||
#sudo apt install tmux
|
getent passwd "$USER" &>/dev/null || { echo "Sorry, but '$USER' does not exist on this system. Exiting."; exit 2; }
|
||||||
#sudo apt install nginx
|
|
||||||
#sudo apt install supervisor
|
# Propose some packages
|
||||||
#sudo apt install ssh
|
echo -e "\nThe following packages are not mandatory, add them as needed.\n"
|
||||||
# todo one day : modify correct path in syncthing.conf
|
declare -a ADD_PACK
|
||||||
#sudo cp syncthing.conf to /etc/supervisor/conf.d/
|
POS_PACK=( htop nginx screen ssh ssl-cert supervisor syncthing tmux )
|
||||||
pip3 install scipy
|
POS_PACK_LEN=${#POS_PACK[@]}
|
||||||
pip3 install numpy
|
for i in ${!POS_PACK[@]} ; do
|
||||||
#pip install pygame==1.9.2
|
pack=${POS_PACK[i]}
|
||||||
#pip3 install pygame==1.9.2
|
read -e -n 1 -p "$((i+1))/${POS_PACK_LEN} Add package ${pack} ? [Y/n]: "
|
||||||
pip3 install redis
|
REPLY=${REPLY:-Y}
|
||||||
pip3 install pysimpledmx
|
[[ "Y" == ${REPLY^^} ]] && ADD_PACK+=(${pack})
|
||||||
pip3 install DMXEnttecPro
|
done
|
||||||
sudo apt install libasound2-dev
|
|
||||||
sudo apt install libjack-dev
|
# Propose the install
|
||||||
pip3 install python-rtmidi
|
PACK_LIST=$( echo "cmake git libasound2-dev libjack-dev libsdl1.2-dev network-manager portaudio19-dev python3-dev python3-pip python3-rtmidi redis-server ${ADD_PACK[@]}" | sort )
|
||||||
pip3 install mido
|
echo -e "\nYou are goind to install:\n$( for pack in ${PACK_LIST[@]}; do echo ' * '$pack; done; )\n"
|
||||||
git clone https://github.com/ptone/pyosc --depth 1 /tmp/pyosc && cd /tmp/pyosc && sudo ./setup.py install
|
read -e -n1 -p "OK? [Y/n]: "
|
||||||
pip3 install tk
|
REPLY=${REPLY:-Y}
|
||||||
cd ../
|
[[ "N" == ${REPLY^^} ]] && exit
|
||||||
python3 configure.py
|
|
||||||
# todo : ask for computer ip and run updateUI.py
|
# Run the install
|
||||||
cd /tmp
|
apt install -y --no-install-recommends $PACK_LIST
|
||||||
sudo apt install portaudio19-dev
|
pip3 install setuptools
|
||||||
sudo apt install cmake
|
pip3 install DMXEnttecPro mido numpy pysimpledmx redis scipy
|
||||||
git clone https://github.com/Ableton/link.git
|
git clone https://github.com/ptone/pyosc --depth 1 /tmp/pyosc && cd /tmp/pyosc && ./setup.py install
|
||||||
cd link
|
cd /tmp && git clone https://github.com/Ableton/link.git && cd link && git submodule update --init --recursive && mkdir build && cd build && cmake .. && cmake --build .
|
||||||
git submodule update --init --recursive
|
cd /tmp/ && git clone --recursive https://github.com/gonzaloflirt/link-python.git && cd link-python && mkdir build && cd build && cmake .. && cmake --build .
|
||||||
mkdir build
|
cd /opt/ && git clone https://git.interhacker.space/teamlaser/LJ
|
||||||
cd build
|
chown -R $USER /opt/LJ
|
||||||
cmake ..
|
|
||||||
cmake --build .
|
|
||||||
cd /tmp/
|
|
||||||
git clone --recursive https://github.com/gonzaloflirt/link-python.git
|
|
||||||
cd link-python
|
|
||||||
mkdir build
|
|
||||||
cd build
|
|
||||||
cmake ..
|
|
||||||
cmake --build .
|
|
||||||
|
Loading…
Reference in New Issue
Block a user