Compare commits
12 Commits
4a9cd73767
...
9ec8d5bc41
Author | SHA1 | Date | |
---|---|---|---|
|
9ec8d5bc41 | ||
|
46962044f1 | ||
|
ed6c0de14d | ||
|
2950eb9f92 | ||
c9cae42ae3 | |||
|
4e88775168 | ||
|
f9578431f9 | ||
|
2221f80dce | ||
|
4c40e2ddf8 | ||
|
f262b5c24b | ||
|
c5c7051ddc | ||
|
56e95c4c4b |
235
ethertools/dac3.py
Executable file
235
ethertools/dac3.py
Executable file
@ -0,0 +1,235 @@
|
||||
# j4cDAC test code
|
||||
#
|
||||
# Copyright 2011 Jacob Potter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import socket
|
||||
import time
|
||||
import struct
|
||||
|
||||
def pack_point(x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
|
||||
"""Pack some color values into a struct dac_point.
|
||||
|
||||
Values must be specified for x, y, r, g, and b. If a value is not
|
||||
passed in for the other fields, i will default to max(r, g, b); the
|
||||
rest default to zero.
|
||||
"""
|
||||
#print(r,g,b)
|
||||
if i < 0:
|
||||
i = max(r, g, b)
|
||||
|
||||
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 = [
|
||||
"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)
|
||||
]
|
||||
for l in lines:
|
||||
print(prefix + l)
|
||||
|
||||
|
||||
class BroadcastPacket(object):
|
||||
"""Represents a broadcast packet from the DAC."""
|
||||
|
||||
def __init__(self, st):
|
||||
"""Initialize from a chunk of data."""
|
||||
self.mac = st[:6]
|
||||
self.hw_rev, self.sw_rev, self.buffer_capacity, \
|
||||
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
|
||||
self.status = Status(st[16:36])
|
||||
|
||||
def dump(self, prefix = " - "):
|
||||
"""Dump to a string."""
|
||||
lines = [
|
||||
"MAC: " + ":".join(
|
||||
"%02x" % (ord(o), ) for o in self.mac),
|
||||
"HW %d, SW %d" %
|
||||
(self.hw_rev, self.sw_rev),
|
||||
"Capabilities: max %d points, %d kpps" %
|
||||
(self.buffer_capacity, self.max_point_rate)
|
||||
]
|
||||
for l in lines:
|
||||
print(prefix + l)
|
||||
self.status.dump(prefix)
|
||||
|
||||
|
||||
class DAC(object):
|
||||
"""A connection to a DAC."""
|
||||
|
||||
def read(self, l):
|
||||
"""Read exactly length bytes from the connection."""
|
||||
while l > len(self.buf):
|
||||
|
||||
buffy = self.conn.recv(4096)
|
||||
#print(buffy)
|
||||
|
||||
self.buf += buffy
|
||||
|
||||
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]
|
||||
cmdR = chr(data[1])
|
||||
status = Status(data[2:])
|
||||
|
||||
status.dump()
|
||||
|
||||
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 __init__(self, host, port = 7765):
|
||||
"""Connect to the DAC over TCP."""
|
||||
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
conn.connect((host, port))
|
||||
conn.settimeout(1)
|
||||
self.conn = conn
|
||||
self.buf = b''
|
||||
|
||||
# Read the "hello" message
|
||||
first_status = self.readresp("?")
|
||||
first_status.dump()
|
||||
|
||||
'''
|
||||
self.conn.sendall('v')
|
||||
self.firmware_string = self.read(32).replace("\x00", " ").strip()
|
||||
print "Firmware: %s" % (self.firmware_string, )
|
||||
'''
|
||||
|
||||
def begin(self, lwm, rate):
|
||||
cmd = struct.pack("<cHI", b'b', lwm, rate)
|
||||
self.conn.sendall(cmd)
|
||||
return self.readresp("b")
|
||||
|
||||
def update(self, 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(*point)
|
||||
|
||||
def write(self, points):
|
||||
epoints = list(map(self.encode_point, points))
|
||||
cmd = struct.pack("<cH", b'd', len(epoints))
|
||||
#print(cmd + b''.join(epoints))
|
||||
self.conn.sendall(cmd + b''.join(epoints))
|
||||
return self.readresp("d")
|
||||
|
||||
def prepare(self):
|
||||
self.conn.sendall('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, stream):
|
||||
# First, prepare the stream
|
||||
if self.last_status.playback_state == 2:
|
||||
raise Exception("already playing?!")
|
||||
elif self.last_status.playback_state == 0:
|
||||
self.prepare()
|
||||
|
||||
started = 0
|
||||
|
||||
while True:
|
||||
# How much room?
|
||||
cap = 1799 - self.last_status.fullness
|
||||
points = stream.read(cap)
|
||||
|
||||
if cap < 100:
|
||||
time.sleep(0.005)
|
||||
cap += 150
|
||||
|
||||
# print "Writing %d points" % (cap, )
|
||||
t0 = time.time()
|
||||
self.write(points)
|
||||
t1 = time.time()
|
||||
# print "Took %f" % (t1 - t0, )
|
||||
|
||||
if not started:
|
||||
self.begin(0, 30000)
|
||||
started = 1
|
||||
|
||||
|
||||
def find_dac():
|
||||
"""Listen for broadcast packets."""
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
bp = BroadcastPacket(data)
|
||||
|
||||
print("Packet from %s: " % (addr, ))
|
||||
bp.dump()
|
||||
|
||||
def find_first_dac():
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
data, addr = s.recvfrom(1024)
|
||||
bp = BroadcastPacket(data)
|
||||
print("Packet from %s: " % (addr, ))
|
||||
return addr[0]
|
35
ethertools/sitter.app/Contents/Info.plist
Executable file
35
ethertools/sitter.app/Contents/Info.plist
Executable file
@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeOSTypes</key>
|
||||
<array>
|
||||
<string>****</string>
|
||||
<string>fold</string>
|
||||
<string>disk</string>
|
||||
</array>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>sitter</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>PythonApplet.icns</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>sitter</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>sitter</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
</dict>
|
||||
</plist>
|
BIN
ethertools/sitter.app/Contents/MacOS/Python
Executable file
BIN
ethertools/sitter.app/Contents/MacOS/Python
Executable file
Binary file not shown.
23
ethertools/sitter.app/Contents/MacOS/sitter
Executable file
23
ethertools/sitter.app/Contents/MacOS/sitter
Executable file
@ -0,0 +1,23 @@
|
||||
#!/System/Library/Frameworks/Python.framework/Versions/2.5/Resources/Python.app/Contents/MacOS/Python
|
||||
|
||||
import sys, os
|
||||
execdir = os.path.dirname(sys.argv[0])
|
||||
executable = os.path.join(execdir, "Python")
|
||||
resdir = os.path.join(os.path.dirname(execdir), "Resources")
|
||||
libdir = os.path.join(os.path.dirname(execdir), "Frameworks")
|
||||
mainprogram = os.path.join(resdir, "__argvemulator_sitter.py")
|
||||
|
||||
sys.argv.insert(1, mainprogram)
|
||||
if 0 or 0:
|
||||
os.environ["PYTHONPATH"] = resdir
|
||||
if 0:
|
||||
os.environ["PYTHONHOME"] = resdir
|
||||
else:
|
||||
pypath = os.getenv("PYTHONPATH", "")
|
||||
if pypath:
|
||||
pypath = ":" + pypath
|
||||
os.environ["PYTHONPATH"] = resdir + pypath
|
||||
os.environ["PYTHONEXECUTABLE"] = executable
|
||||
os.environ["DYLD_LIBRARY_PATH"] = libdir
|
||||
os.environ["DYLD_FRAMEWORK_PATH"] = libdir
|
||||
os.execve(executable, sys.argv, os.environ)
|
1
ethertools/sitter.app/Contents/PkgInfo
Executable file
1
ethertools/sitter.app/Contents/PkgInfo
Executable file
@ -0,0 +1 @@
|
||||
APPL????
|
BIN
ethertools/sitter.app/Contents/Resources/PythonApplet.icns
Executable file
BIN
ethertools/sitter.app/Contents/Resources/PythonApplet.icns
Executable file
Binary file not shown.
4
ethertools/sitter.app/Contents/Resources/__argvemulator_sitter.py
Executable file
4
ethertools/sitter.app/Contents/Resources/__argvemulator_sitter.py
Executable file
@ -0,0 +1,4 @@
|
||||
import argvemulator, os
|
||||
|
||||
argvemulator.ArgvCollector().mainloop()
|
||||
execfile(os.path.join(os.path.split(__file__)[0], "sitter.py"))
|
414
ethertools/sitter.app/Contents/Resources/sitter.py
Executable file
414
ethertools/sitter.app/Contents/Resources/sitter.py
Executable file
@ -0,0 +1,414 @@
|
||||
#!/usr/bin/python
|
||||
# j4cDAC "sitter"
|
||||
#
|
||||
# Copyright 2012 Jacob Potter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import socket
|
||||
import time
|
||||
import struct
|
||||
|
||||
def pack_point(x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
|
||||
"""Pack some color values into a struct dac_point.
|
||||
|
||||
Values must be specified for x, y, r, g, and b. If a value is not
|
||||
passed in for the other fields, i will default to max(r, g, b); the
|
||||
rest default to zero.
|
||||
"""
|
||||
|
||||
if i < 0:
|
||||
i = max(r, g, b)
|
||||
|
||||
return struct.pack("<HhhHHHHHH", flags, x, y, i, r, g, b, 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 = [
|
||||
"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)
|
||||
]
|
||||
for l in lines:
|
||||
print prefix + l
|
||||
|
||||
|
||||
class BroadcastPacket(object):
|
||||
"""Represents a broadcast packet from the DAC."""
|
||||
|
||||
def __init__(self, st, ip=None):
|
||||
"""Initialize from a chunk of data."""
|
||||
self.mac = st[:6]
|
||||
self.hw_rev, self.sw_rev, self.buffer_capacity, \
|
||||
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
|
||||
self.status = Status(st[16:36])
|
||||
self.ip = ip
|
||||
|
||||
def dump(self, prefix = " - "):
|
||||
"""Dump to a string."""
|
||||
lines = [
|
||||
"MAC: " + ":".join(
|
||||
"%02x" % (ord(o), ) for o in self.mac),
|
||||
"HW %d, SW %d" %
|
||||
(self.hw_rev, self.sw_rev),
|
||||
"Capabilities: max %d points, %d kpps" %
|
||||
(self.buffer_capacity, self.max_point_rate)
|
||||
]
|
||||
for l in lines:
|
||||
print prefix + l
|
||||
self.status.dump(prefix)
|
||||
|
||||
def macstr(self):
|
||||
return "".join("%02x" % (ord(c), ) for c in self.mac)
|
||||
|
||||
|
||||
class DAC(object):
|
||||
"""A connection to a DAC."""
|
||||
|
||||
def got_broadcast(self, bp):
|
||||
self.last_broadcast = bp
|
||||
self.last_broadcast_time = time.time()
|
||||
|
||||
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]
|
||||
cmdR = data[1]
|
||||
status = Status(data[2:])
|
||||
|
||||
# status.dump()
|
||||
|
||||
if cmdR != cmd:
|
||||
raise ProtocolError("expected resp for %r, got %r"
|
||||
% (cmd, cmdR))
|
||||
|
||||
if response != "a":
|
||||
raise ProtocolError("expected ACK, got %r"
|
||||
% (response, ))
|
||||
|
||||
self.last_status = status
|
||||
return status
|
||||
|
||||
def __init__(self, macstr, bp):
|
||||
self.macstr = macstr
|
||||
self.firmware_string = "-"
|
||||
self.got_broadcast(bp)
|
||||
|
||||
try:
|
||||
t1 = time.time()
|
||||
self.connect(self.last_broadcast.ip[0])
|
||||
t = time.time() - t1
|
||||
self.conn_status = "ok (%d ms)" % (t * 500)
|
||||
|
||||
if self.last_broadcast.sw_rev < 2:
|
||||
self.firmware_string = "(old)"
|
||||
else:
|
||||
self.conn.sendall('v')
|
||||
self.firmware_string = self.read(32).replace("\x00", " ").strip()
|
||||
except e:
|
||||
self.conn_status = str(e)
|
||||
|
||||
def connect(self, host, port = 7765):
|
||||
"""Connect to the DAC over TCP."""
|
||||
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
conn.settimeout(0.2)
|
||||
conn.connect((host, port))
|
||||
self.conn = conn
|
||||
self.buf = ""
|
||||
|
||||
# Read the "hello" message
|
||||
first_status = self.readresp("?")
|
||||
first_status.dump()
|
||||
|
||||
|
||||
|
||||
def begin(self, lwm, rate):
|
||||
cmd = struct.pack("<cHI", "b", lwm, rate)
|
||||
self.conn.sendall(cmd)
|
||||
return self.readresp("b")
|
||||
|
||||
def update(self, lwm, rate):
|
||||
cmd = struct.pack("<cHI", "u", lwm, rate)
|
||||
self.conn.sendall(cmd)
|
||||
return self.readresp("u")
|
||||
|
||||
def encode_point(self, point):
|
||||
return pack_point(*point)
|
||||
|
||||
def write(self, points):
|
||||
epoints = map(self.encode_point, points)
|
||||
cmd = struct.pack("<cH", "d", len(epoints))
|
||||
self.conn.sendall(cmd + "".join(epoints))
|
||||
return self.readresp("d")
|
||||
|
||||
def prepare(self):
|
||||
self.conn.sendall("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, stream):
|
||||
# First, prepare the stream
|
||||
if self.last_status.playback_state == 2:
|
||||
raise Exception("already playing?!")
|
||||
elif self.last_status.playback_state == 0:
|
||||
self.prepare()
|
||||
|
||||
started = 0
|
||||
|
||||
while True:
|
||||
# How much room?
|
||||
cap = 1799 - self.last_status.fullness
|
||||
points = stream.read(cap)
|
||||
|
||||
if cap < 100:
|
||||
time.sleep(0.005)
|
||||
cap += 150
|
||||
|
||||
# print "Writing %d points" % (cap, )
|
||||
t0 = time.time()
|
||||
self.write(points)
|
||||
t1 = time.time()
|
||||
# print "Took %f" % (t1 - t0, )
|
||||
|
||||
if not started:
|
||||
self.begin(0, 30000)
|
||||
started = 1
|
||||
|
||||
|
||||
def find_dac():
|
||||
"""Listen for broadcast packets."""
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
bp = BroadcastPacket(data)
|
||||
|
||||
print "Packet from %s: " % (addr, )
|
||||
bp.dump()
|
||||
|
||||
|
||||
from Tkinter import *
|
||||
import socket
|
||||
import Queue
|
||||
import thread
|
||||
import time
|
||||
|
||||
class DacDisplay(LabelFrame):
|
||||
def __init__(self, master):
|
||||
LabelFrame.__init__(self, master, width=600, height=400)
|
||||
self.grid_propagate(0)
|
||||
|
||||
Label(self, text="IP Address:").grid(row=0, column=0, sticky=N+E)
|
||||
Label(self, text="Version:").grid(row=1, column=0, sticky=N+E)
|
||||
Label(self, text="Status:").grid(row=2, column=0, sticky=N+E)
|
||||
Label(self, text="Source:").grid(row=3, column=0, sticky=N+E)
|
||||
Label(self, text="Network:").grid(row=4, column=0, sticky=N+E)
|
||||
Label(self, text="Firmware:").grid(row=5, column=0, sticky=N+E)
|
||||
|
||||
self.iplabel = Label(self, text = "")
|
||||
self.iplabel.grid(row=0, column=1, sticky=N+W)
|
||||
self.verslabel = Label(self, text = "")
|
||||
self.verslabel.grid(row=1, column=1, sticky=N+W)
|
||||
self.stlabel = Label(self, text = "")
|
||||
self.stlabel.grid(row=2, column=1, sticky=N+W)
|
||||
self.srclabel = Label(self, text = "")
|
||||
self.srclabel.grid(row=3, column=1, sticky=N+W)
|
||||
self.netlabel = Label(self, text = "")
|
||||
self.netlabel.grid(row=4, column=1, sticky=N+W)
|
||||
self.fwlabel = Label(self, text = "")
|
||||
self.fwlabel.grid(row=5, column=1, sticky=N+W)
|
||||
|
||||
self.display_none()
|
||||
|
||||
def display_none(self):
|
||||
self['text'] = "Ether Dream"
|
||||
|
||||
for l in self.iplabel, self.verslabel, self.stlabel, self.srclabel, self.netlabel, self.fwlabel:
|
||||
l['text'] = ""
|
||||
|
||||
def display_dac(self, dac):
|
||||
b = dac.last_broadcast
|
||||
self['text'] = "Ether Dream " + b.macstr()[6:]
|
||||
self.iplabel['text'] = str(b.ip[0])
|
||||
self.verslabel['text'] = "hardware %d, software %d" % (b.hw_rev, b.sw_rev)
|
||||
|
||||
st_str = ""
|
||||
if b.status.le_state == 0:
|
||||
st_str = "online, "
|
||||
else:
|
||||
st_str = "ESTOP ACTIVE (%d), " % (b.status.le_flags, )
|
||||
|
||||
if b.status.playback_state == 0:
|
||||
st_str += "idle"
|
||||
elif b.status.playback_state == 1:
|
||||
st_str += "prepared"
|
||||
elif b.status.playback_state == 2:
|
||||
st_str += "playing (%d buffered, %d played)" % (b.status.fullness, b.status.point_count)
|
||||
else:
|
||||
st_str += "DAC state %d" % (b.status.playback_state, )
|
||||
|
||||
if b.status.point_rate:
|
||||
st_str += ", %d pps" % (b.status.point_rate)
|
||||
|
||||
self.stlabel['text'] = st_str
|
||||
|
||||
if b.status.source == 0:
|
||||
src_str = "network"
|
||||
elif b.status.source == 1:
|
||||
src_str = "file playback: %s, repeat %s" % (
|
||||
b.status.source_flags & 1 and "playing" or "not playing",
|
||||
b.status.source_flags & 2 and "on" or "off"
|
||||
)
|
||||
elif b.status.source == 2:
|
||||
src_str = "abstract generator: %s" % (
|
||||
b.status.source_flags & 1 and "playing" or "not playing",
|
||||
)
|
||||
else:
|
||||
src_str = "unknown %d" % (b.status.source)
|
||||
|
||||
self.srclabel['text'] = src_str
|
||||
self.netlabel['text'] = dac.conn_status
|
||||
self.fwlabel['text'] = dac.firmware_string
|
||||
|
||||
class DacTracker(Listbox):
|
||||
def __init__(self, master, *args, **kwargs):
|
||||
Listbox.__init__(self, master, *args, **kwargs)
|
||||
|
||||
self.dac_list = []
|
||||
self.dac_macstr_map = {}
|
||||
|
||||
self.bind("<<ListboxSelect>>", self.update_selection)
|
||||
|
||||
def update_selection(self, lb=None):
|
||||
if self.dac_display:
|
||||
try:
|
||||
dac_obj = self.dac_list[self.index(ACTIVE)]
|
||||
except:
|
||||
return
|
||||
self.dac_display.display_dac(dac_obj)
|
||||
|
||||
def got_packet(self, bp):
|
||||
macstr = bp.macstr()
|
||||
if macstr not in self.dac_macstr_map:
|
||||
new_dac = DAC(macstr, bp)
|
||||
self.insert(END, macstr[6:])
|
||||
self.dac_list.append(new_dac)
|
||||
self.dac_macstr_map[macstr] = new_dac
|
||||
dac_obj = new_dac
|
||||
else:
|
||||
dac_obj = self.dac_macstr_map[macstr]
|
||||
dac_obj.got_broadcast(bp)
|
||||
|
||||
if len(self.dac_list) == 1:
|
||||
self.selection_set(0)
|
||||
self.update_selection()
|
||||
|
||||
def check_on_dac():
|
||||
if time.time() - dac_obj.last_broadcast_time < 2:
|
||||
return
|
||||
|
||||
idx = self.dac_list.index(dac_obj)
|
||||
self.dac_list.remove(dac_obj)
|
||||
del self.dac_macstr_map[macstr]
|
||||
self.delete(idx)
|
||||
self.dac_display.display_none()
|
||||
|
||||
|
||||
self.after(2000, check_on_dac)
|
||||
|
||||
|
||||
|
||||
# Set up the basic window
|
||||
root = Tk()
|
||||
root.title("Ether Dream")
|
||||
root.resizable(FALSE, FALSE)
|
||||
frame = Frame(root)
|
||||
frame.grid()
|
||||
|
||||
disp = DacDisplay(root)
|
||||
disp.grid(row=0, column=1, padx=5, pady=5)
|
||||
tracker = DacTracker(root, height=22)
|
||||
tracker.grid(row=0, column=0, padx=5, pady=5)
|
||||
tracker.dac_display = disp
|
||||
|
||||
# Set up queue checker
|
||||
packet_queue = Queue.Queue()
|
||||
def queue_check():
|
||||
try:
|
||||
while True:
|
||||
data, addr = packet_queue.get_nowait()
|
||||
tracker.got_packet(BroadcastPacket(data, addr))
|
||||
except Queue.Empty:
|
||||
root.after(100, queue_check)
|
||||
|
||||
root.after(100, queue_check)
|
||||
|
||||
# Set up listening socket and thread
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
def socket_thread():
|
||||
while True:
|
||||
packet_queue.put(s.recvfrom(1024))
|
||||
thread.start_new(socket_thread, ())
|
||||
|
||||
root.mainloop()
|
414
ethertools/sitter.py
Executable file
414
ethertools/sitter.py
Executable file
@ -0,0 +1,414 @@
|
||||
#!/usr/bin/python
|
||||
# j4cDAC "sitter"
|
||||
#
|
||||
# Copyright 2012 Jacob Potter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import socket
|
||||
import time
|
||||
import struct
|
||||
|
||||
def pack_point(x, y, r, g, b, i = -1, u1 = 0, u2 = 0, flags = 0):
|
||||
"""Pack some color values into a struct dac_point.
|
||||
|
||||
Values must be specified for x, y, r, g, and b. If a value is not
|
||||
passed in for the other fields, i will default to max(r, g, b); the
|
||||
rest default to zero.
|
||||
"""
|
||||
|
||||
if i < 0:
|
||||
i = max(r, g, b)
|
||||
|
||||
return struct.pack("<HhhHHHHHH", flags, x, y, i, r, g, b, 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 = [
|
||||
"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)
|
||||
]
|
||||
for l in lines:
|
||||
print prefix + l
|
||||
|
||||
|
||||
class BroadcastPacket(object):
|
||||
"""Represents a broadcast packet from the DAC."""
|
||||
|
||||
def __init__(self, st, ip=None):
|
||||
"""Initialize from a chunk of data."""
|
||||
self.mac = st[:6]
|
||||
self.hw_rev, self.sw_rev, self.buffer_capacity, \
|
||||
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
|
||||
self.status = Status(st[16:36])
|
||||
self.ip = ip
|
||||
|
||||
def dump(self, prefix = " - "):
|
||||
"""Dump to a string."""
|
||||
lines = [
|
||||
"MAC: " + ":".join(
|
||||
"%02x" % (ord(o), ) for o in self.mac),
|
||||
"HW %d, SW %d" %
|
||||
(self.hw_rev, self.sw_rev),
|
||||
"Capabilities: max %d points, %d kpps" %
|
||||
(self.buffer_capacity, self.max_point_rate)
|
||||
]
|
||||
for l in lines:
|
||||
print prefix + l
|
||||
self.status.dump(prefix)
|
||||
|
||||
def macstr(self):
|
||||
return "".join("%02x" % (ord(c), ) for c in self.mac)
|
||||
|
||||
|
||||
class DAC(object):
|
||||
"""A connection to a DAC."""
|
||||
|
||||
def got_broadcast(self, bp):
|
||||
self.last_broadcast = bp
|
||||
self.last_broadcast_time = time.time()
|
||||
|
||||
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]
|
||||
cmdR = data[1]
|
||||
status = Status(data[2:])
|
||||
|
||||
# status.dump()
|
||||
|
||||
if cmdR != cmd:
|
||||
raise ProtocolError("expected resp for %r, got %r"
|
||||
% (cmd, cmdR))
|
||||
|
||||
if response != "a":
|
||||
raise ProtocolError("expected ACK, got %r"
|
||||
% (response, ))
|
||||
|
||||
self.last_status = status
|
||||
return status
|
||||
|
||||
def __init__(self, macstr, bp):
|
||||
self.macstr = macstr
|
||||
self.firmware_string = "-"
|
||||
self.got_broadcast(bp)
|
||||
|
||||
try:
|
||||
t1 = time.time()
|
||||
self.connect(self.last_broadcast.ip[0])
|
||||
t = time.time() - t1
|
||||
self.conn_status = "ok (%d ms)" % (t * 500)
|
||||
|
||||
if self.last_broadcast.sw_rev < 2:
|
||||
self.firmware_string = "(old)"
|
||||
else:
|
||||
self.conn.sendall('v')
|
||||
self.firmware_string = self.read(32).replace("\x00", " ").strip()
|
||||
except Exception, e:
|
||||
self.conn_status = str(e)
|
||||
|
||||
def connect(self, host, port = 7765):
|
||||
"""Connect to the DAC over TCP."""
|
||||
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
conn.settimeout(0.2)
|
||||
conn.connect((host, port))
|
||||
self.conn = conn
|
||||
self.buf = ""
|
||||
|
||||
# Read the "hello" message
|
||||
first_status = self.readresp("?")
|
||||
first_status.dump()
|
||||
|
||||
|
||||
|
||||
def begin(self, lwm, rate):
|
||||
cmd = struct.pack("<cHI", "b", lwm, rate)
|
||||
self.conn.sendall(cmd)
|
||||
return self.readresp("b")
|
||||
|
||||
def update(self, lwm, rate):
|
||||
cmd = struct.pack("<cHI", "u", lwm, rate)
|
||||
self.conn.sendall(cmd)
|
||||
return self.readresp("u")
|
||||
|
||||
def encode_point(self, point):
|
||||
return pack_point(*point)
|
||||
|
||||
def write(self, points):
|
||||
epoints = map(self.encode_point, points)
|
||||
cmd = struct.pack("<cH", "d", len(epoints))
|
||||
self.conn.sendall(cmd + "".join(epoints))
|
||||
return self.readresp("d")
|
||||
|
||||
def prepare(self):
|
||||
self.conn.sendall("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, stream):
|
||||
# First, prepare the stream
|
||||
if self.last_status.playback_state == 2:
|
||||
raise Exception("already playing?!")
|
||||
elif self.last_status.playback_state == 0:
|
||||
self.prepare()
|
||||
|
||||
started = 0
|
||||
|
||||
while True:
|
||||
# How much room?
|
||||
cap = 1799 - self.last_status.fullness
|
||||
points = stream.read(cap)
|
||||
|
||||
if cap < 100:
|
||||
time.sleep(0.005)
|
||||
cap += 150
|
||||
|
||||
# print "Writing %d points" % (cap, )
|
||||
t0 = time.time()
|
||||
self.write(points)
|
||||
t1 = time.time()
|
||||
# print "Took %f" % (t1 - t0, )
|
||||
|
||||
if not started:
|
||||
self.begin(0, 30000)
|
||||
started = 1
|
||||
|
||||
|
||||
def find_dac():
|
||||
"""Listen for broadcast packets."""
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
bp = BroadcastPacket(data)
|
||||
|
||||
print "Packet from %s: " % (addr, )
|
||||
bp.dump()
|
||||
|
||||
|
||||
from Tkinter import *
|
||||
import socket
|
||||
import Queue
|
||||
import thread
|
||||
import time
|
||||
|
||||
class DacDisplay(LabelFrame):
|
||||
def __init__(self, master):
|
||||
LabelFrame.__init__(self, master, width=600, height=400)
|
||||
self.grid_propagate(0)
|
||||
|
||||
Label(self, text="IP Address:").grid(row=0, column=0, sticky=N+E)
|
||||
Label(self, text="Version:").grid(row=1, column=0, sticky=N+E)
|
||||
Label(self, text="Status:").grid(row=2, column=0, sticky=N+E)
|
||||
Label(self, text="Source:").grid(row=3, column=0, sticky=N+E)
|
||||
Label(self, text="Network:").grid(row=4, column=0, sticky=N+E)
|
||||
Label(self, text="Firmware:").grid(row=5, column=0, sticky=N+E)
|
||||
|
||||
self.iplabel = Label(self, text = "")
|
||||
self.iplabel.grid(row=0, column=1, sticky=N+W)
|
||||
self.verslabel = Label(self, text = "")
|
||||
self.verslabel.grid(row=1, column=1, sticky=N+W)
|
||||
self.stlabel = Label(self, text = "")
|
||||
self.stlabel.grid(row=2, column=1, sticky=N+W)
|
||||
self.srclabel = Label(self, text = "")
|
||||
self.srclabel.grid(row=3, column=1, sticky=N+W)
|
||||
self.netlabel = Label(self, text = "")
|
||||
self.netlabel.grid(row=4, column=1, sticky=N+W)
|
||||
self.fwlabel = Label(self, text = "")
|
||||
self.fwlabel.grid(row=5, column=1, sticky=N+W)
|
||||
|
||||
self.display_none()
|
||||
|
||||
def display_none(self):
|
||||
self['text'] = "Ether Dream"
|
||||
|
||||
for l in self.iplabel, self.verslabel, self.stlabel, self.srclabel, self.netlabel, self.fwlabel:
|
||||
l['text'] = ""
|
||||
|
||||
def display_dac(self, dac):
|
||||
b = dac.last_broadcast
|
||||
self['text'] = "Ether Dream " + b.macstr()[6:]
|
||||
self.iplabel['text'] = str(b.ip[0])
|
||||
self.verslabel['text'] = "hardware %d, software %d" % (b.hw_rev, b.sw_rev)
|
||||
|
||||
st_str = ""
|
||||
if b.status.le_state == 0:
|
||||
st_str = "online, "
|
||||
else:
|
||||
st_str = "ESTOP ACTIVE (%d), " % (b.status.le_flags, )
|
||||
|
||||
if b.status.playback_state == 0:
|
||||
st_str += "idle"
|
||||
elif b.status.playback_state == 1:
|
||||
st_str += "prepared"
|
||||
elif b.status.playback_state == 2:
|
||||
st_str += "playing (%d buffered, %d played)" % (b.status.fullness, b.status.point_count)
|
||||
else:
|
||||
st_str += "DAC state %d" % (b.status.playback_state, )
|
||||
|
||||
if b.status.point_rate:
|
||||
st_str += ", %d pps" % (b.status.point_rate)
|
||||
|
||||
self.stlabel['text'] = st_str
|
||||
|
||||
if b.status.source == 0:
|
||||
src_str = "network"
|
||||
elif b.status.source == 1:
|
||||
src_str = "file playback: %s, repeat %s" % (
|
||||
b.status.source_flags & 1 and "playing" or "not playing",
|
||||
b.status.source_flags & 2 and "on" or "off"
|
||||
)
|
||||
elif b.status.source == 2:
|
||||
src_str = "abstract generator: %s" % (
|
||||
b.status.source_flags & 1 and "playing" or "not playing",
|
||||
)
|
||||
else:
|
||||
src_str = "unknown %d" % (b.status.source)
|
||||
|
||||
self.srclabel['text'] = src_str
|
||||
self.netlabel['text'] = dac.conn_status
|
||||
self.fwlabel['text'] = dac.firmware_string
|
||||
|
||||
class DacTracker(Listbox):
|
||||
def __init__(self, master, *args, **kwargs):
|
||||
Listbox.__init__(self, master, *args, **kwargs)
|
||||
|
||||
self.dac_list = []
|
||||
self.dac_macstr_map = {}
|
||||
|
||||
self.bind("<<ListboxSelect>>", self.update_selection)
|
||||
|
||||
def update_selection(self, lb=None):
|
||||
if self.dac_display:
|
||||
try:
|
||||
dac_obj = self.dac_list[self.index(ACTIVE)]
|
||||
except:
|
||||
return
|
||||
self.dac_display.display_dac(dac_obj)
|
||||
|
||||
def got_packet(self, bp):
|
||||
macstr = bp.macstr()
|
||||
if macstr not in self.dac_macstr_map:
|
||||
new_dac = DAC(macstr, bp)
|
||||
self.insert(END, macstr[6:])
|
||||
self.dac_list.append(new_dac)
|
||||
self.dac_macstr_map[macstr] = new_dac
|
||||
dac_obj = new_dac
|
||||
else:
|
||||
dac_obj = self.dac_macstr_map[macstr]
|
||||
dac_obj.got_broadcast(bp)
|
||||
|
||||
if len(self.dac_list) == 1:
|
||||
self.selection_set(0)
|
||||
self.update_selection()
|
||||
|
||||
def check_on_dac():
|
||||
if time.time() - dac_obj.last_broadcast_time < 2:
|
||||
return
|
||||
|
||||
idx = self.dac_list.index(dac_obj)
|
||||
self.dac_list.remove(dac_obj)
|
||||
del self.dac_macstr_map[macstr]
|
||||
self.delete(idx)
|
||||
self.dac_display.display_none()
|
||||
|
||||
|
||||
self.after(2000, check_on_dac)
|
||||
|
||||
|
||||
|
||||
# Set up the basic window
|
||||
root = Tk()
|
||||
root.title("Ether Dream")
|
||||
root.resizable(FALSE, FALSE)
|
||||
frame = Frame(root)
|
||||
frame.grid()
|
||||
|
||||
disp = DacDisplay(root)
|
||||
disp.grid(row=0, column=1, padx=5, pady=5)
|
||||
tracker = DacTracker(root, height=22)
|
||||
tracker.grid(row=0, column=0, padx=5, pady=5)
|
||||
tracker.dac_display = disp
|
||||
|
||||
# Set up queue checker
|
||||
packet_queue = Queue.Queue()
|
||||
def queue_check():
|
||||
try:
|
||||
while True:
|
||||
data, addr = packet_queue.get_nowait()
|
||||
tracker.got_packet(BroadcastPacket(data, addr))
|
||||
except Queue.Empty:
|
||||
root.after(100, queue_check)
|
||||
|
||||
root.after(100, queue_check)
|
||||
|
||||
# Set up listening socket and thread
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
def socket_thread():
|
||||
while True:
|
||||
packet_queue.put(s.recvfrom(1024))
|
||||
thread.start_new(socket_thread, ())
|
||||
|
||||
root.mainloop()
|
95
ethertools/talk3.py
Executable file
95
ethertools/talk3.py
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# j4cDAC test code
|
||||
#
|
||||
# Copyright 2011 Jacob Potter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dac3 as dac
|
||||
|
||||
class SquarePointStream(object):
|
||||
'''
|
||||
def produce(self):
|
||||
pmax = 15600
|
||||
pstep = 100
|
||||
cmax = 65535
|
||||
while True:
|
||||
for x in xrange(-pmax, pmax, pstep):
|
||||
yield (x, pmax, cmax, 0, 0, cmax)
|
||||
for y in xrange(pmax, -pmax, -pstep):
|
||||
yield (pmax, y, 0, cmax, 0, cmax)
|
||||
for x in xrange(pmax, -pmax, -pstep):
|
||||
yield (x, -pmax, 0, 0, cmax, cmax)
|
||||
for y in xrange(-pmax, pmax, pstep):
|
||||
yield (-pmax, y, cmax, cmax, cmax, cmax)
|
||||
'''
|
||||
|
||||
def produce(self):
|
||||
|
||||
pmax = 15600
|
||||
pstep = 100
|
||||
Cmax = 65535
|
||||
|
||||
|
||||
while True:
|
||||
|
||||
print("Cmax:",Cmax)
|
||||
|
||||
for x in range(-pmax, pmax, pstep):
|
||||
|
||||
yield (x, pmax, Cmax, 0, 0) # pure Red
|
||||
#yield (x, pmax, 0, Cmax, 0) # pure Green
|
||||
#yield (x, pmax, 0, 0, Cmax) # pure Blue
|
||||
#yield (x, pmax, Cmax, Cmax, Cmax) # pure White
|
||||
|
||||
|
||||
for y in range(pmax, -pmax, -pstep):
|
||||
|
||||
#yield (pmax, y, Cmax, 0, 0) # pure Red
|
||||
yield (pmax, y, 0, Cmax, 0) # pure Green
|
||||
#yield (pmax, y, 0, 0, Cmax) # pure Blue
|
||||
#yield (pmax, y, Cmax, Cmax, Cmax) # pure White
|
||||
|
||||
for x in range(pmax, -pmax, -pstep):
|
||||
#yield (x, -pmax, Cmax, 0, 0) # pure Red
|
||||
#yield (x, -pmax, 0, Cmax, 0) # pure Green
|
||||
yield (x, -pmax, 0, 0, Cmax) # pure Blue
|
||||
#yield (x, -pmax, Cmax, Cmax, Cmax) # pure White
|
||||
|
||||
|
||||
for y in range(-pmax, pmax, pstep):
|
||||
#yield (-pmax, y, Cmax, 0, 0) # pure Red
|
||||
#yield (-pmax, y,0, Cmax, 0) # pure Green
|
||||
#yield (-pmax, y, 0, 0, Cmax) # pure Blue
|
||||
yield (-pmax, y, Cmax, Cmax, Cmax) # pure White
|
||||
|
||||
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.stream = self.produce()
|
||||
|
||||
def read(self, n):
|
||||
return [next(self.stream) for i in range(n)]
|
||||
|
||||
class NullPointStream(object):
|
||||
def read(self, n):
|
||||
return [(0, 0, 0, 0, 0)] * n
|
||||
|
||||
#dac.find_dac()
|
||||
|
||||
d = dac.DAC(dac.find_first_dac())
|
||||
#d = dac.DAC("192.168.1.43")
|
||||
|
||||
d.play_stream(SquarePointStream())
|
21
ethertools/watch.py
Executable file
21
ethertools/watch.py
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# j4cDAC test code
|
||||
#
|
||||
# Copyright 2011 Jacob Potter
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, version 3.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import dac
|
||||
|
||||
dac.find_dac()
|
@ -1,986 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
|
||||
'''
|
||||
|
||||
lj23layers v0.7.6 for LJ v0.8+
|
||||
|
||||
LJ functions (API) for python plugins/clients
|
||||
|
||||
"layers" version :
|
||||
- "PL" has been replaced by "layer"
|
||||
- "Client"
|
||||
|
||||
Each program using LJ should declare itself by call lj23layers Config :
|
||||
|
||||
Config(redisIP, client number, name)
|
||||
|
||||
|
||||
Basic Draw :
|
||||
|
||||
- PolyLineOneColor, rPolyLineOneColor, LineTo, Line
|
||||
- PolyLineRGB, rPolyLineRGB, LineRGBTo, LineRGB
|
||||
- rgb2int(r,g,b)
|
||||
- Drawlayer (point list number) : once you stacked all wanted elements, like 2 polylines, send them to lasers.
|
||||
- DrawDests(): Draw all requested destinations for each layer .
|
||||
|
||||
High level draw :
|
||||
|
||||
- Text(word, integercolor, layer , xpos, ypos, resize, rotx, roty, rotz) : Display a word
|
||||
- TextRGB(word, red, green, blue, ...)
|
||||
- Embeded font1
|
||||
|
||||
|
||||
Laser objects (name and convenient group of parameters for one or several point lists)
|
||||
|
||||
- RelativeObject
|
||||
- FixedObject
|
||||
|
||||
"Destinations" : Tell for given Layer a scene/Laser ("destination").
|
||||
Each Layer can have different destination (i.e to display same stuff on different laser)
|
||||
|
||||
|
||||
OSC and plugins functions :
|
||||
|
||||
SendLJ(adress,message) : LJ remote control. See commands.py
|
||||
SendResol(address,message): Send OSC message to Resolume.
|
||||
WebStatus(message) : display message on webui
|
||||
|
||||
Ljscene(client): Change scene number in redis keys
|
||||
Ljlayer(layer): Change layer number in redis keys = laser target.
|
||||
ClosePlugin(name): Send UI closing info of given plugin
|
||||
|
||||
OSCstart(): Start the OSC system.
|
||||
OSCframe(): Handle incoming OSC message. Calling the right callback
|
||||
OSCstop(): Properly close the OSC system
|
||||
OSCping(): /ping Answer to LJ pings by sending /pong name
|
||||
OSCquit(): /quit Exit calling script using name in terminal
|
||||
OSCadddest(): layer , scene, laser Add a destination
|
||||
OSCdeldest(): layer , scene, lasers delete a destination
|
||||
OSCobj(): /name/obj objectname attribute value for automation
|
||||
OSCvar(): /name/var variablename value for automation
|
||||
|
||||
|
||||
Joystick management is removed. Get it back in todolist
|
||||
|
||||
setup_controls(joystick)
|
||||
|
||||
XboxController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
|
||||
Ps3Controller : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger, getUp, getDown, getLeft, getRight, getFire1, getFire2(self):
|
||||
MySaitekController : getLeftHori,getLeftVert, getRightHori,getRightVert, getLeftTrigger,getRightTrigger
|
||||
MyThrustController : getLeftHori, getLeftVert, getRightHori, getRightVert, getLeftTrigger, getRightTrigger
|
||||
CSLController : getLeftHori,getLeftVert,getRightHori, getRightVert,getLeftTrigger,getRightTrigger,getFire1,getFire2
|
||||
my USB Joystick : getUp,getDown,getLeft,getRight,etLeftTrigger, getRightTrigger,getFire1, getFire2
|
||||
|
||||
|
||||
|
||||
Class management manuals:
|
||||
|
||||
https://stackoverflow.com/questions/739882/iterating-over-object-instances-of-a-given-class-in-python
|
||||
https://stackoverflow.com/questions/8628123/counting-instances-of-a-class
|
||||
http://effbot.org/pyfaq/how-do-i-get-a-list-of-all-instances-of-a-given-class.htm
|
||||
|
||||
|
||||
LICENCE : CC
|
||||
Sam Neurohack
|
||||
|
||||
'''
|
||||
|
||||
import math
|
||||
import redis
|
||||
import sys
|
||||
import weakref
|
||||
import struct
|
||||
import numpy as np
|
||||
import gstt
|
||||
from multiprocessing import Process, Queue, TimeoutError
|
||||
|
||||
is_py2 = sys.version[0] == '2'
|
||||
if is_py2:
|
||||
from OSC import OSCServer, OSCClient, OSCMessage
|
||||
#print ("Importing lj23 and OSC from libs...")
|
||||
else:
|
||||
from OSC3 import OSCServer, OSCClient, OSCMessage
|
||||
#print ("Importing lj23 and OSC3 from libs...")
|
||||
|
||||
|
||||
#redisIP = '127.0.0.1'
|
||||
#r = redis.StrictRedis(host=redisIP, port=6379, db=0)
|
||||
|
||||
ClientNumber = 0
|
||||
name = "noname"
|
||||
oscrun = True
|
||||
point_list = []
|
||||
layers = [[],[],[],[],[],[],[],[],[],[]]
|
||||
|
||||
fft3Groups = [-1,-1,-1,-1]
|
||||
|
||||
Dests = dict()
|
||||
|
||||
oscIPresol = "127.0.0.1"
|
||||
oscPORTresol = 7000
|
||||
|
||||
# 3D to 2D projection parameters
|
||||
fov = 256
|
||||
viewer_distance = 2.2
|
||||
|
||||
|
||||
'''
|
||||
|
||||
Laser "objects"
|
||||
|
||||
|
||||
set a name and convenient group of parameters for one or several point lists
|
||||
|
||||
RelativeObject is for point lists around 0,0 with builtin move/rotation.
|
||||
|
||||
How to init with object color, xpos,... :
|
||||
osciObj = lj.RelativeObject('osciObj', True, 255, [], white, red, green,blue,0 , False, centerX , centerY , 1 , Xrot , Yrot , Zrot)
|
||||
How to use in drawing functions : you're free to use 0, some or all of any laserobject attributes
|
||||
- draw one or several pointlists with 'A' laserobject color and 'B' laserobject xpos ypos ?
|
||||
- Change color of 'main' object and all other objects using it will change also
|
||||
how to change attribute :
|
||||
osciObj.resize = 2 or /pluginame/change 'OsciObj' 'resize' 2
|
||||
|
||||
'''
|
||||
|
||||
class RelativeObject:
|
||||
|
||||
kind = 'relative'
|
||||
counter = 0
|
||||
|
||||
def __init__(self, name, active, intensity, xy, color, red, green, blue, layer , closed, xpos , ypos , resize , rotx , roty , rotz):
|
||||
self.name = name
|
||||
self.active = active # True/False
|
||||
self.intensity = intensity
|
||||
self.xy = [] # Dots list
|
||||
self.color = color # RGB color in int
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
self.layer = layer
|
||||
self.closed = closed
|
||||
self.xpos = xpos
|
||||
self.ypos = ypos
|
||||
self.resize = resize
|
||||
self.rotx = rotx
|
||||
self.roty = roty
|
||||
self.rotz = rotz
|
||||
|
||||
RelativeObject.counter += 1
|
||||
#type(self).counter += 1
|
||||
|
||||
def __del__(self):
|
||||
RelativeObject.counter -= 1
|
||||
|
||||
|
||||
# Fixed Laser object : point list in 'pygame' space (top left = 0,0 / bottom right)
|
||||
class FixedObject:
|
||||
|
||||
kind = 'fixed'
|
||||
counter = 0
|
||||
|
||||
def __init__(self, name, intensity, active, xy, color, red, green, blue, layer , closed):
|
||||
self.name = name
|
||||
self.active = active # True/False
|
||||
self.intensity = intensity
|
||||
self.xy = []
|
||||
self.color = color
|
||||
self.red = red
|
||||
self.green = green
|
||||
self.blue = blue
|
||||
self.layer = layer
|
||||
self.closed = closed
|
||||
|
||||
FixedObject.counter += 1
|
||||
|
||||
def __del__(self):
|
||||
FixedObject.counter -= 1
|
||||
|
||||
'''
|
||||
|
||||
class IterDest(type):
|
||||
def __new__ (cls, name, bases, dct):
|
||||
dct['_instances'] = []
|
||||
return super().__new__(cls, name, bases, dct)
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
instance = super().__call__(*args, **kwargs)
|
||||
cls._instances.append(instance)
|
||||
return instance
|
||||
|
||||
def __iter__(cls):
|
||||
return iter(cls._instances)
|
||||
|
||||
class DestObject():
|
||||
|
||||
# class Destinations(metaclass=IterDest):
|
||||
__metaclass__ = IterDest
|
||||
counter = 0
|
||||
def __init__(self, name, number, active, layer , scene, laser):
|
||||
self.name = name
|
||||
self.number = number
|
||||
self.active = active
|
||||
self.layer = layer
|
||||
self.scene = scene
|
||||
self.laser = laser
|
||||
|
||||
DestObject.counter += 1
|
||||
|
||||
def __del__(self):
|
||||
DestObject.counter -= 1
|
||||
'''
|
||||
class DestObject():
|
||||
|
||||
# class Destinations(metaclass=IterDest):
|
||||
_instances = set()
|
||||
counter = 0
|
||||
|
||||
def __init__(self, name, number, active, layer , scene, laser):
|
||||
self.name = name
|
||||
self.number = number
|
||||
self.active = active
|
||||
self.layer = layer
|
||||
self.scene = scene
|
||||
self.laser = laser
|
||||
self._instances.add(weakref.ref(self))
|
||||
DestObject.counter += 1
|
||||
|
||||
@classmethod
|
||||
def getinstances(cls):
|
||||
dead = set()
|
||||
for ref in cls._instances:
|
||||
obj = ref()
|
||||
if obj is not None:
|
||||
yield obj
|
||||
else:
|
||||
dead.add(ref)
|
||||
cls._instances -= dead
|
||||
|
||||
|
||||
def __del__(self):
|
||||
DestObject.counter -= 1
|
||||
|
||||
|
||||
|
||||
def Config(redIP,client,myname):
|
||||
global ClientNumber, name, redisIP, r
|
||||
|
||||
redisIP = redIP
|
||||
r = redis.StrictRedis(host=redisIP, port=6379, db=0)
|
||||
|
||||
# ClientNumber 255 are not drawing anything like artnet
|
||||
ClientNumber = client
|
||||
#print ("client configured",ClientNumber)
|
||||
name = myname
|
||||
print ("lj23layers : Plugin declare its name :",name)
|
||||
#print layer
|
||||
return r
|
||||
|
||||
|
||||
def LjClient(client):
|
||||
global ClientNumber
|
||||
|
||||
ClientNumber = client
|
||||
|
||||
|
||||
|
||||
def Ljlayer(somelayer):
|
||||
global layer
|
||||
|
||||
layer = somelayer
|
||||
|
||||
|
||||
def fromRedis(n):
|
||||
|
||||
encoded = r.get(n)
|
||||
#print("")
|
||||
#print('fromredis key',n,":",encoded)
|
||||
h, w = struct.unpack('>II',encoded[:8])
|
||||
#print("fromredis array size",n,":",h,w)
|
||||
a = np.frombuffer(encoded, dtype=np.int16, offset=8).reshape(h,w)
|
||||
#print("fromredis array",n,":",a)
|
||||
return a
|
||||
|
||||
# Store Numpy array 'a' in Redis key 'n'
|
||||
# Write also in redis key 'a' numpy array, its 2 dimensions size : h time w values
|
||||
def toRedis(n,a):
|
||||
|
||||
#print("array.shape", a.shape, len(a.shape) )
|
||||
if len(a.shape) == 1:
|
||||
h = a.shape[0]
|
||||
w = 1
|
||||
else:
|
||||
h,w = a.shape
|
||||
#print("toredis", n,"h",h,"w",w,"a",a)
|
||||
shape = struct.pack('>II',h,w)
|
||||
|
||||
#shape = struct.pack('>II',len(a),1)
|
||||
#print("toredis",n,a)
|
||||
encoded = shape + a.tobytes()
|
||||
|
||||
# Store encoded data in Redis
|
||||
return r.set(n,encoded)
|
||||
|
||||
|
||||
#
|
||||
# OSC functions
|
||||
#
|
||||
|
||||
# OSC clients
|
||||
|
||||
def SendLJ(oscaddress,oscargs=''):
|
||||
|
||||
oscmsg = OSCMessage()
|
||||
oscmsg.setAddress(oscaddress)
|
||||
oscmsg.append(oscargs)
|
||||
|
||||
osclientlj = OSCClient()
|
||||
osclientlj.connect((redisIP, 8002))
|
||||
|
||||
print("lj23layers for",name,"sending OSC message :", oscmsg, "to", redisIP, ":8002")
|
||||
if gstt.debug >0:
|
||||
print("lj23layers for",name,"sending OSC message :", oscmsg, "to", redisIP, ":8002")
|
||||
try:
|
||||
osclientlj.sendto(oscmsg, (redisIP, 8002))
|
||||
oscmsg.clearData()
|
||||
except:
|
||||
print ('Connection to LJ refused : died ?')
|
||||
pass
|
||||
#time.sleep(0.001
|
||||
|
||||
|
||||
|
||||
# Resolume OSC Arena client.
|
||||
# sendresol(oscaddress, [arg1, arg2,...])
|
||||
# example : sendresol("/noteon",note)
|
||||
|
||||
def SendResol(oscaddress,oscargs):
|
||||
|
||||
oscmsg = OSCMessage()
|
||||
oscmsg.setAddress(oscaddress)
|
||||
oscmsg.append(oscargs)
|
||||
|
||||
osclientresol = OSCClient()
|
||||
osclientresol.connect((oscIPresol, oscPORTresol))
|
||||
|
||||
print("lj23layers sending OSC message : ", oscmsg, "to Resolume", oscIPresol, ":", oscPORTresol)
|
||||
try:
|
||||
osclientresol.sendto(oscmsg, (oscIPresol, oscPORTresol))
|
||||
oscmsg.clearData()
|
||||
except:
|
||||
print ('Connection to Resolume refused : died ?')
|
||||
pass
|
||||
|
||||
|
||||
def SendIntensity(laser, intensity):
|
||||
r.set('/intensity/' + str(laser), str(intensity))
|
||||
r.set('/order/'+str(laser), 6)
|
||||
SendLJ("/kpps/" + str(layer)+ " " + str(int(args[1])))
|
||||
|
||||
|
||||
def Sendkpps(laser, kpps):
|
||||
r.set('/kpps/' + str(laser), str(kpps))
|
||||
r.set('/order/'+str(laser), 7)
|
||||
|
||||
|
||||
def WebStatus(message):
|
||||
SendLJ("/status", message)
|
||||
|
||||
|
||||
# Closing plugin messages to LJ
|
||||
def ClosePlugin():
|
||||
WebStatus(name+" Exiting")
|
||||
SendLJ("/"+name+"/start",0)
|
||||
|
||||
|
||||
|
||||
|
||||
# RAW OSC Frame available ?
|
||||
def OSCframe():
|
||||
# clear timed_out flag
|
||||
#print "oscframe"
|
||||
oscserver.timed_out = False
|
||||
# handle all pending requests then return
|
||||
while not oscserver.timed_out:
|
||||
oscserver.handle_request()
|
||||
|
||||
# Answer to LJ pings with /pong value
|
||||
def OSCping(path, tags, args, source):
|
||||
#def OSCping():
|
||||
if gstt.debug >0:
|
||||
print(name, "lj23layers got /ping from LJ -> reply /pong", name)
|
||||
SendLJ("/pong",name)
|
||||
|
||||
# Properly close the system. Todo
|
||||
def OSCstop():
|
||||
oscserver.close()
|
||||
|
||||
|
||||
# /quit
|
||||
def OSCquit(path, tags, args, source):
|
||||
global oscrun
|
||||
|
||||
oscrun = False
|
||||
print('lj23layers got /quit for',name)
|
||||
#WebStatus(name + " quit.")
|
||||
#SendLJ("/"+name+"/start",0)
|
||||
#print("Stopping OSC...")
|
||||
#OSCstop()
|
||||
#sys.exit()
|
||||
|
||||
|
||||
# default handler
|
||||
def OSChandler(path, tags, args, source):
|
||||
|
||||
oscaddress = ''.join(path.split("/"))
|
||||
print("lj23layers Default OSC Handler for",name,": msg from Client :" + str(source[0]),)
|
||||
print("OSC address", path)
|
||||
if len(args) > 0:
|
||||
print("with args", args)
|
||||
|
||||
#oscIPout = str(source[0])
|
||||
#osclient.connect((oscIPout, oscPORTout))
|
||||
|
||||
|
||||
# for any laser object : /pluginame/obj objectname attribute value
|
||||
# like : /pluginname/obj 'fft' 'xpos' 100
|
||||
# attributes for all lj Objects: name, xy_list, c, layer
|
||||
# + for RelativeObjects : closed, xpos , ypos , resize , rotx , roty , rotz
|
||||
def OSCobj(path, tags, args, source):
|
||||
|
||||
obj = eval(args[0]+"."+ args[1])
|
||||
obj = args[2]
|
||||
|
||||
|
||||
def OSCvar(path, tags, args, source):
|
||||
|
||||
obj = eval(args[0])
|
||||
obj = args[1]
|
||||
|
||||
|
||||
def addOSCdefaults(server):
|
||||
global oscserver
|
||||
|
||||
oscserver = server
|
||||
oscserver.addMsgHandler( "default", OSChandler )
|
||||
oscserver.addMsgHandler( "/ping", OSCping)
|
||||
oscserver.addMsgHandler( "/quit", OSCquit)
|
||||
oscserver.addMsgHandler( "/"+ name + "/adddest", OSCadddest)
|
||||
oscserver.addMsgHandler( "/"+ name + "/deldest", OSCdeldest)
|
||||
oscserver.addMsgHandler( "/"+ name + "/dest", OSCdest)
|
||||
oscserver.addMsgHandler( "/"+ name + "/obj", OSCobj)
|
||||
oscserver.addMsgHandler( "/"+ name + "/var", OSCvar)
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Color functions
|
||||
#
|
||||
|
||||
# input hexcode = '0xff00ff'
|
||||
def hex2rgb(hexcode):
|
||||
|
||||
hexcode = hexcode[2:]
|
||||
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
|
||||
#return tuple(map(ord,hexcode[1:].decode('hex')))
|
||||
|
||||
# input rgb=(255,0,255) output '0xff00ff'
|
||||
#def rgb2hex(rgb):
|
||||
# return '0x%02x%02x%02x' % tuple(rgb)
|
||||
|
||||
def rgb2hex(r, g, b):
|
||||
return hex((r << 16) + (g << 8) + b)
|
||||
|
||||
|
||||
#def rgb2int(rgb):
|
||||
# return int('0x%02x%02x%02x' % tuple(rgb),0)
|
||||
|
||||
def rgb2int(r,g,b):
|
||||
return int('0x%02x%02x%02x' % (r,g,b),0)
|
||||
|
||||
def int2rgb(intcode):
|
||||
#hexcode = '0x{0:06X}'.format(intcode)
|
||||
hexcode = '{0:06X}'.format(intcode)
|
||||
return tuple(int(hexcode[i:i+2], 16) for i in (0, 2, 4))
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Drawing basic functions
|
||||
#
|
||||
|
||||
# Lines
|
||||
def Line(xy1, xy2, c, layer ):
|
||||
LineTo(xy1, 0, layer )
|
||||
LineTo(xy2, c , layer )
|
||||
|
||||
def rLine(xy1, xy2, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
|
||||
rLineTo(xy1, 0, layer )
|
||||
rLineTo(xy2, c , layer )
|
||||
|
||||
def LineRGB(xy1, xy2, red,green,blue, layer ):
|
||||
|
||||
LineTo(xy1, 0, layer )
|
||||
LineTo(xy2, int('0x%02x%02x%02x' % (red,green,blue),0) , layer )
|
||||
|
||||
|
||||
# Lineto
|
||||
def LineTo(xy, c, layer ):
|
||||
|
||||
layers[layer].append((xy + (c,)))
|
||||
|
||||
def LineRGBTo(xy, red, green, blue, layer ):
|
||||
|
||||
LineTo(xy, int('0x%02x%02x%02x' % (red,green,blue),0), layer )
|
||||
|
||||
def rLineTo(xy, c, layer , xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
|
||||
|
||||
layers[layer ].append((Pointransf(xy, xpos, ypos, resize, rotx, roty, rotz) + (c,)))
|
||||
|
||||
|
||||
# Polylines
|
||||
def PolyLineOneColor(xy_list, c, layer , closed ):
|
||||
#print "--"
|
||||
#print "c",c
|
||||
#print "xy_list",xy_list
|
||||
#print "--"
|
||||
xy0 = None
|
||||
for xy in xy_list:
|
||||
if xy0 is None:
|
||||
xy0 = xy
|
||||
#print "xy0:",xy0
|
||||
LineTo(xy0,0, layer )
|
||||
LineTo(xy0,c, layer )
|
||||
else:
|
||||
#print "xy:",xy
|
||||
LineTo(xy,c, layer )
|
||||
if closed:
|
||||
LineTo(xy0,c, layer )
|
||||
|
||||
def PolyLineRGB(xy_list, red, green, blue, layer , closed ):
|
||||
|
||||
PolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), layer , closed )
|
||||
|
||||
|
||||
# rPolylines
|
||||
# Send 2D point list around 0,0 with 3D rotation resizing and reposition around xpos ypos
|
||||
#def rPolyLineOneColor(self, xy_list, c, layer , closed, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
|
||||
def rPolyLineOneColor(xy_list, c, layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
|
||||
xy0 = None
|
||||
for xy in xy_list:
|
||||
print(xy,xy0)
|
||||
if xy0 is None:
|
||||
xy0 = xy
|
||||
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), 0, layer )
|
||||
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), c, layer )
|
||||
else:
|
||||
LineTo(Pointransf(xy, xpos, ypos, resize, rotx, roty, rotz), c, layer )
|
||||
if closed:
|
||||
LineTo(Pointransf(xy0, xpos, ypos, resize, rotx, roty, rotz), c, layer )
|
||||
|
||||
def rPolyLineRGB(xy_list, red, green, blue, layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0):
|
||||
|
||||
rPolyLineOneColor(xy_list, int('0x%02x%02x%02x' % (red,green,blue),0), layer , closed, xpos = 0, ypos =0, resize =0.7, rotx =0, roty =0 , rotz=0)
|
||||
|
||||
|
||||
|
||||
# Computing points coordinates for rPolyline function from 3D and around 0,0 to pygame coordinates
|
||||
def Pointransf(xy, xpos = 0, ypos =0, resize =1, rotx =0, roty =0 , rotz=0):
|
||||
|
||||
x = xy[0] * resize
|
||||
y = xy[1] * resize
|
||||
z = xy[2] * resize
|
||||
|
||||
rad = math.radians(rotx)
|
||||
cosaX = math.cos(rad)
|
||||
sinaX = math.sin(rad)
|
||||
|
||||
y2 = y
|
||||
y = y2 * cosaX - z * sinaX
|
||||
z = y2 * sinaX + z * cosaX
|
||||
|
||||
rad = math.radians(roty)
|
||||
cosaY = math.cos(rad)
|
||||
sinaY = math.sin(rad)
|
||||
|
||||
z2 = z
|
||||
z = z2 * cosaY - x * sinaY
|
||||
x = z2 * sinaY + x * cosaY
|
||||
|
||||
rad = math.radians(rotz)
|
||||
cosZ = math.cos(rad)
|
||||
sinZ = math.sin(rad)
|
||||
|
||||
x2 = x
|
||||
x = x2 * cosZ - y * sinZ
|
||||
y = x2 * sinZ + y * cosZ
|
||||
|
||||
#print xy, (x + xpos,y+ ypos)
|
||||
#return (x + xpos, y + ypos)
|
||||
|
||||
#to understand why it get negative Y
|
||||
""" Transforms this 3D point to 2D using a perspective projection. """
|
||||
factor = fov / (viewer_distance + z)
|
||||
x = x * factor + xpos
|
||||
y = y * factor + ypos
|
||||
#y = - y * factor + ypos
|
||||
return (x, y)
|
||||
|
||||
|
||||
def Lineslayer(layer):
|
||||
print("Stupido !! your code is to old : use Drawlayer() instead of LinesPL()")
|
||||
Drawlayer(layer )
|
||||
|
||||
|
||||
def Draw(layer):
|
||||
#print '/pl/0/'+str(layer), str(layers[layer])
|
||||
if r.set('/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])) == True:
|
||||
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
|
||||
layers[layer] = []
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def Resetlayer(self, layer):
|
||||
layers[layer] = []
|
||||
|
||||
|
||||
#
|
||||
# "Destinations" management for layers
|
||||
#
|
||||
|
||||
|
||||
# Add a destination for a given layer
|
||||
def Addest(layer, scene, laser):
|
||||
|
||||
print (name,'adding',layer,scene,laser,'?')
|
||||
if Findest(layer, scene, laser) == -1:
|
||||
newdest = DestsObjects.counter + 1
|
||||
Dest0 = lj.DestObject(str(newdest), newdest, True, layer , scene, laser)
|
||||
print("New destination added with number", newdest)
|
||||
else:
|
||||
print("Destination already existed")
|
||||
|
||||
|
||||
# OSC add a destination for a given layer
|
||||
# /pluginame/dest layer, scene, laser
|
||||
def OSCadddest(path, tags, args, source):
|
||||
|
||||
Addests(int(args[0]),int(args[1]),int(args[2]))
|
||||
|
||||
|
||||
# Find layer destination with its parameters in destinations dictionnary
|
||||
def Findest(layer, scene, laser):
|
||||
|
||||
print(name, 'searching layer,scene,laser',layer,scene,laser)
|
||||
for item in DestObjects.getinstances():
|
||||
#print(item)
|
||||
if item.layer == layer and item.scene == scene and item.laser == laser:
|
||||
#Dests.append(item[0])
|
||||
print('found number',item.number)
|
||||
return item.number
|
||||
else:
|
||||
print('no destination found')
|
||||
return -1
|
||||
'''
|
||||
#Dests = list()
|
||||
allDests = Dests.items()
|
||||
for item in allDests:
|
||||
print(item)
|
||||
if item[1] == layer and item[2] == scene and item[3] == laser:
|
||||
#Dests.append(item[0])
|
||||
return Dests[item[0]]
|
||||
else:
|
||||
return -1
|
||||
'''
|
||||
|
||||
# Find and remove a layer destination with its parameters in destinations dictionnary
|
||||
def Deldest(layer, scene, laser):
|
||||
|
||||
Destnumber = Findest(layer, scene, laser)
|
||||
print(name,'deleting Destination layer, scene, laser', layer,scene, laser)
|
||||
|
||||
if Destnumber != -1:
|
||||
print('found DestObject', Destnumber)
|
||||
delattr(DestObjects, str(Destnumber))
|
||||
print("Destination", Destnumber,"was removed")
|
||||
else:
|
||||
print("Destination was not found")
|
||||
|
||||
|
||||
# OSC Delete a destination for a given layer
|
||||
# /pluginame/deldests layer, scene, laser
|
||||
def OSCdeldest(path, tags, args, source):
|
||||
|
||||
Deldests(args[0], args[1], args[2])
|
||||
|
||||
|
||||
# pluginame/dest layer, scene, laser
|
||||
def OSCdest(path, tags, args, source):
|
||||
|
||||
# For single layer plugin : add a new destination
|
||||
Addest(0, args[0], args[1])
|
||||
|
||||
# For single layer plugin : remove a destination
|
||||
|
||||
# For multiple layers plugin : add or remove
|
||||
|
||||
|
||||
# Replace Drawlayer if Destinations paradigm is implemented in plugin code
|
||||
def DrawDests():
|
||||
|
||||
# Objects style
|
||||
|
||||
#print("DrawDest")
|
||||
|
||||
for destination in DestObject.getinstances():
|
||||
|
||||
#print(Dests[str(destination)])
|
||||
#print('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), ":", str(layers[Dests[str(destination)]["PL"]]))
|
||||
#print(len(layers[destination.layer]))
|
||||
if destination.active == True:
|
||||
if r.set('/pl/'+str(destination.scene)+'/'+str(destination.laser), str(layers[destination.layer])) == True:
|
||||
#print ('layer', destination.layer, '/pl/'+str(destination.scene)+'/'+str(destination.laser), str(layers[destination.layer]))
|
||||
pass
|
||||
else:
|
||||
print('Redis key modification failed')
|
||||
|
||||
# Maybe one layer can be sent to multiple destination so they are all reset *after* all sending.
|
||||
for layerss in range(4):
|
||||
|
||||
layers[layerss] = []
|
||||
|
||||
'''
|
||||
# Dictionnary style
|
||||
|
||||
#print(Dests)
|
||||
for destination in range(len(Dests)):
|
||||
#print(Dests[str(destination)])
|
||||
#print('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), ":", str(layers[Dests[str(destination)]["layer"]]))
|
||||
if r.set('/pl/'+str(Dests[str(destination)]["scene"])+'/'+str(Dests[str(destination)]["laser"]), str(layers[Dests[str(destination)]["layer"]])) == True:
|
||||
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
|
||||
pass
|
||||
else:
|
||||
print('Redis key modification failed')
|
||||
|
||||
# Maybe one layer can be sent to multiple destination so they are all reset *after* all sending.
|
||||
for destination in range(len(Dests)):
|
||||
|
||||
layers[Dests[str(destination)]["layer"]] = []
|
||||
'''
|
||||
'''
|
||||
scenes = 4
|
||||
|
||||
def DrawDestslayer(layer):
|
||||
|
||||
for scene in range(scenes):
|
||||
|
||||
if Dests[laser]["scene"] != -1:
|
||||
if r.set('/pl/'+str(Dests[laser]["scene"])+'/'+str(Dests[laser]["laser"]), str(layers[Dests[laser]["laser"]])) == True:
|
||||
if r.set('/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])) == True:
|
||||
#print '/pl/'+str(ClientNumber)+'/'+str(layer), str(layers[layer])
|
||||
layers[Dests[laser]["laser"]] = []
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
'''
|
||||
|
||||
#
|
||||
# High level drawing functions
|
||||
#
|
||||
|
||||
|
||||
# Font1
|
||||
|
||||
|
||||
ASCII_GRAPHICS = [
|
||||
|
||||
#implementé
|
||||
|
||||
[(-50,30), (-30,-30), (30,-30), (10,30), (-50,30)], # 0
|
||||
[(-20,30), (0,-30), (-20,30)], # 1
|
||||
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # 2
|
||||
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # 3
|
||||
[(30,10), (-30,10), (0,-30), (0,30)], # 4
|
||||
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # 5
|
||||
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # 6
|
||||
[(-30,-30), (30,-30), (-30,30)], # 7
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], # 8
|
||||
[(30,0), (-30,0), (-30,-10), (0,-30), (30,-30), (30,10), (0,30), (-30,30)], # 9
|
||||
|
||||
# A implementer
|
||||
[(-30,10), (30,-10), (30,10), (0,30), (-30,10), (-30,-10), (0,-30), (30,-10)], #:
|
||||
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # ;
|
||||
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # <
|
||||
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # =
|
||||
[(30,10), (-30,10), (0,-30), (0,30)], # >
|
||||
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # ?
|
||||
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # @
|
||||
|
||||
# Implementé 65-90
|
||||
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30), (-30,0), (30,0)], # B
|
||||
[(30,30), (-30,30), (-30,-30), (30,-30)], # C
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], # D
|
||||
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # E
|
||||
[(-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # F
|
||||
[(0,0), (30,0), (30,30), (-30,30), (-30,-30),(30,-30)], # G
|
||||
[(-30,-30), (-30,30), (-30,0), (30,0), (30,30), (30,-30)], # H
|
||||
[(0,30), (0,-30)], # I
|
||||
[(-30,30), (0,-30), (0,-30), (-30,-30), (30,-30)], # J
|
||||
[(-30,-30), (-30,30), (-30,0), (30,-30), (-30,0), (30,30)], # K
|
||||
[(30,30), (-30,30), (-30,-30)], # L
|
||||
[(-30,30), (-30,-30), (0,0), (30,-30), (30,30)], # M
|
||||
[(-30,30), (-30,-30), (30,30), (30,-30)], # N
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (-30,30)], # O
|
||||
[(-30,0), (30,0), (30,-30), (-30,-30), (-30,30)], # P
|
||||
[(30,30), (30,-30), (-30,-30), (-30,30), (30,30),(35,35)], # Q
|
||||
[(-30,30), (-30,-30), (30,-30), (30,0), (-30,0), (30,30)], # R
|
||||
[(30,-30), (-30,-30), (-30,0), (30,0), (30,30), (-30,30)], # S
|
||||
[(0,30), (0,-30), (-30,-30), (30,-30)], # T
|
||||
[(-30,-30), (-30,30), (30,30), (30,-30)], # U
|
||||
[(-30,-30), (0,30), (30,-30)], # V
|
||||
[(-30,-30), (-30,30), (0,0), (30,30), (30,-30)], # W
|
||||
[(-30,30), (30,-30), (-30,-30), (30,30)], # X
|
||||
[(0,30), (0,0), (30,-30), (0,0), (-30,-30)], # Y
|
||||
[(30,30), (-30,30), (30,-30), (-30,-30)], # Z
|
||||
|
||||
# A implementer
|
||||
|
||||
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # [
|
||||
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # \
|
||||
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], # ]
|
||||
[(30,10), (-30,10), (0,-30), (0,30)], # ^
|
||||
[(30,-30), (-30,-30), (-30,0), (0,0), (30,10), (0,30), (-30,30)], # _
|
||||
[(30,-30), (0,-30), (-30,-10), (-30,30), (0,30), (30,10), (30,0), (-30,0)], # `
|
||||
|
||||
# Implementé 97-122
|
||||
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20), (-20,0), (20,0)], # b
|
||||
[(20,20), (-20,20), (-20,-20), (20,-20)], # c
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # d
|
||||
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # e
|
||||
[(-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # f
|
||||
[(0,0), (20,0), (20,20), (-20,20), (-20,-20),(20,-20)], # g
|
||||
[(-20,-20), (-20,20), (-20,0), (20,0), (20,20), (20,-20)], # h
|
||||
[(0,20), (0,-20)], # i
|
||||
[(-20,20), (0,-20), (0,-20), (-20,-20), (20,-20)], # j
|
||||
[(-20,-20), (-20,20), (-20,0), (20,-20), (-20,0), (20,20)], # k
|
||||
[(20,20), (-20,20), (-20,-20)], # l
|
||||
[(-20,20), (-20,-20), (0,0), (20,-20), (20,20)], # m
|
||||
[(-20,20), (-20,-20), (20,20), (20,-20)], # n
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
|
||||
[(-20,0), (20,0), (20,-20), (-20,-20), (-20,20)], # p
|
||||
[(20,20), (20,-20), (-20,-20), (-20,20), (20,20),(25,25)], # q
|
||||
[(-20,20), (-20,-20), (20,-20), (20,0), (-20,0), (20,20)], # r
|
||||
[(20,-20), (-20,-20), (-20,0), (20,0), (20,20), (-20,20)], # s
|
||||
[(0,20), (0,-20), (-20,-20), (20,-20)], # t
|
||||
[(-20,-20), (-20,20), (20,20), (20,-20)], # u
|
||||
[(-20,-20), (0,20), (20,-20)], # v
|
||||
[(-20,-20), (-20,20), (0,0), (20,20), (20,-20)], # w
|
||||
[(-20,20), (20,-20)], [(-20,-20), (20,20)], # x
|
||||
[(0,20), (0,0), (20,-20), (0,0), (-20,-20)], # y
|
||||
[(20,20), (-20,20), (20,-20), (-20,-20)], # z
|
||||
|
||||
# A implementer
|
||||
[(-2,15), (2,15)], # Point a la place de {
|
||||
[(-30,-10), (0,-30), (0,30)], [(-30,30), (30,30)], # |
|
||||
[(-30,-10), (0,-30), (30,-10), (30,0), (-30,30), (30,30)], # }
|
||||
[(-30,-30), (0,-30), (30,-10), (0,0), (30,10), (0,30), (-30,30)], #
|
||||
[(30,10), (-30,10), (0,-30), (0,30)], # DEL
|
||||
|
||||
# Accents 128-151 a implementer
|
||||
[(30,30), (-30,30), (-30,-30), (30,-30)], # C
|
||||
[(-20,-20), (-20,20), (20,20), (20,-20)], # û
|
||||
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # â
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # ä
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
|
||||
[(20,20), (-20,20), (-20,-20), (20,-20)], # c
|
||||
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
|
||||
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
|
||||
[(20,20), (-20,20), (-20,-0), (20,0), (-20,0), (-20,-20), (20,-20)], # é
|
||||
[(0,20), (0,-20)], # i
|
||||
[(0,20), (0,-20)], # i
|
||||
[(0,20), (0,-20)], # i
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
|
||||
[(-30,30), (-30,-30), (30,-30), (30,30), (30,0), (-30,0)], # A
|
||||
[(30,30), (-30,30), (-30,-0), (30,0), (-30,0), (-30,-30), (30,-30)], # E
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (20,0), (-20,0)], # a
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
|
||||
[(-20,20), (-20,-20), (20,-20), (20,20), (-20,20)], # o
|
||||
[(-20,-20), (-20,20), (20,20), (20,-20)], # u
|
||||
[(-20,-20), (-20,20), (20,20), (20,-20)] # u
|
||||
|
||||
]
|
||||
|
||||
|
||||
def DigitsDots(number,color):
|
||||
dots =[]
|
||||
for dot in ASCII_GRAPHICS[number]:
|
||||
#print dot
|
||||
dots.append((gstt.xy_center[0]+dot[0],gstt.xy_center[1]+dot[1],color))
|
||||
#self.point_list.append((xy + (c,)))
|
||||
return dots
|
||||
|
||||
def CharDots(char,color):
|
||||
|
||||
dots =[]
|
||||
for dot in ASCII_GRAPHICS[ord(char)-46]:
|
||||
dots.append((dot[0],dot[1],color))
|
||||
return dots
|
||||
|
||||
def Text(message, c, layer, xpos, ypos, resize, rotx, roty, rotz):
|
||||
|
||||
dots =[]
|
||||
|
||||
l = len(message)
|
||||
i= 0
|
||||
#print()
|
||||
# print (message)
|
||||
|
||||
for ch in message:
|
||||
|
||||
#print ""
|
||||
# texte centre en x automatiquement selon le nombre de lettres l
|
||||
x_offset = 26 * (- (0.9*l) + 3*i)
|
||||
# Digits
|
||||
if ord(ch)<58:
|
||||
char_layer_list = ASCII_GRAPHICS[ord(ch) - 48]
|
||||
|
||||
# Uppercase
|
||||
elif 64 < ord(ch) < 91 :
|
||||
char_layer_list = ASCII_GRAPHICS[ord(ch) - 46]
|
||||
|
||||
# Lowercase
|
||||
elif 96 < ord(ch) < 123 :
|
||||
char_layer_list = ASCII_GRAPHICS[ord(ch) - 45]
|
||||
|
||||
char_draw = []
|
||||
#dots.append((char_layer_list[0][0] + x_offset,char_layer_list[0][1],0))
|
||||
|
||||
for xy in char_layer_list:
|
||||
char_draw.append((xy[0] + x_offset,xy[1],c))
|
||||
i +=1
|
||||
#print ch,char_layer_list,char_draw
|
||||
rPolyLineOneColor(char_draw, c, layer , False, xpos, ypos, resize, rotx, roty, rotz)
|
||||
#dots.append(char_draw)
|
||||
|
||||
def TextRGB(message,c, layer, xpos, ypos, resize, rotx, roty, rotz):
|
||||
|
||||
Text(message,int('0x%02x%02x%02x' % (red,green,blue),0), layer, xpos, ypos, resize, rotx, roty, rotz)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,542 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# -*- mode: Python -*-
|
||||
|
||||
'''
|
||||
|
||||
Tracer
|
||||
for LJ v0.8.2
|
||||
|
||||
Enhanced version (support for several lasers) of the etherdream python library from j4cDAC.
|
||||
One tracer process is launched per requested laser. I/O based on redis keys.
|
||||
|
||||
LICENCE : CC
|
||||
Sam Neurohack, pclf
|
||||
|
||||
Uses redis keys value for live inputs (Pointlists to draw,...) and outputs (DAC state, errors,..).
|
||||
Most of redis keys are read and set at each main loop.
|
||||
Includes live conversion in etherdream coordinates, geometric corrections,...
|
||||
Etherdream IP is found in conf file for given laser number. (LJ.conf)
|
||||
|
||||
Redis keys to draw things :
|
||||
/order select some change to adjust. See below
|
||||
/pl/lasernumber [(x,y,color),(x1,y1,color),...] A string of list of points list.
|
||||
/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
|
||||
|
||||
|
||||
Redis keys for Etherdream DAC
|
||||
|
||||
- Control
|
||||
/kpps see order 7
|
||||
/intensity see order 6
|
||||
/red see order 8
|
||||
/green see order 8
|
||||
/blue see order 8
|
||||
|
||||
- DAC status report
|
||||
/lstt/lasernumber value etherdream last_status.playback_state (0: idle 1: prepare 2: playing)
|
||||
/cap/lasernumber value number of empty points sent to fill etherdream buffer (up to 1799)
|
||||
/lack/lasernumber value "a": ACK "F": Full "I": invalid. 64 or 35 for no connection.
|
||||
|
||||
|
||||
|
||||
Order
|
||||
|
||||
0 : Draw Normal point list
|
||||
1 : Get the new EDH
|
||||
2 : Draw BLACK point list
|
||||
3 : Draw GRID point list
|
||||
4 : Resampler Change (longs and shorts lsteps)
|
||||
5 : Client Key change
|
||||
6 : Intensity change
|
||||
7 : kpps change
|
||||
8 : color balance change
|
||||
|
||||
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 libs3 import homographyp
|
||||
import numpy as np
|
||||
import binascii
|
||||
|
||||
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)]
|
||||
|
||||
r = redis.StrictRedis(host=gstt.LjayServerIP, port=6379, db=0)
|
||||
# r = redis.StrictRedis(host=gstt.LjayServerIP , port=6379, db=0, password='-+F816Y+-')
|
||||
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", '64': "NOCONNECTION ?" }
|
||||
|
||||
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
|
||||
|
||||
|
||||
#print("Tracer", laser,":", r,g,b,"intensity", intensity, "i", i)
|
||||
#print(x, type(x), int(x))
|
||||
return struct.pack("<HhhHHHHHH", flags, int(x), int(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 BroadcastPacket(object):
|
||||
"""Represents a broadcast packet from the DAC."""
|
||||
|
||||
def __init__(self, st):
|
||||
"""Initialize from a chunk of data."""
|
||||
self.mac = st[:6]
|
||||
self.hw_rev, self.sw_rev, self.buffer_capacity, \
|
||||
self.max_point_rate = struct.unpack("<HHHI", st[6:16])
|
||||
self.status = Status(st[16:36])
|
||||
|
||||
def dump(self, prefix = " - "):
|
||||
"""Dump to a string."""
|
||||
lines = [
|
||||
"MAC: " + ":".join(
|
||||
"%02x" % (ord(o), ) for o in self.mac),
|
||||
"HW %d, SW %d" %
|
||||
(self.hw_rev, self.sw_rev),
|
||||
"Capabilities: max %d points, %d kpps" %
|
||||
(self.buffer_capacity, self.max_point_rate)
|
||||
]
|
||||
print()
|
||||
for l in lines:
|
||||
print(prefix + l)
|
||||
if gstt.debug > 1:
|
||||
self.status.dump(prefix)
|
||||
|
||||
|
||||
class DAC(object):
|
||||
"""A connection to a DAC."""
|
||||
|
||||
|
||||
# "Laser point List" Point generator
|
||||
# each points is yielded : Getpoints() call n times OnePoint()
|
||||
|
||||
def OnePoint(self):
|
||||
|
||||
while True:
|
||||
|
||||
#pdb.set_trace()
|
||||
for indexpoint,currentpoint in enumerate(self.pl):
|
||||
#print indexpoint, currentpoint
|
||||
xyc = [currentpoint[0],currentpoint[1],currentpoint[2]]
|
||||
self.xyrgb = self.EtherPoint(xyc)
|
||||
|
||||
delta_x, delta_y = self.xyrgb[0] - self.xyrgb_prev[0], self.xyrgb[1] - self.xyrgb_prev[1]
|
||||
|
||||
#test adaptation selon longueur ligne
|
||||
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
|
||||
|
||||
for e in l_steps:
|
||||
step = e[0]
|
||||
|
||||
for i in range(0,e[1]):
|
||||
|
||||
self.xyrgb_step = (self.xyrgb_prev[0] + step*delta_x, self.xyrgb_prev[1] + step*delta_y) + self.xyrgb[2:]
|
||||
yield self.xyrgb_step
|
||||
|
||||
self.xyrgb_prev = self.xyrgb
|
||||
|
||||
|
||||
def GetPoints(self, n):
|
||||
|
||||
d = [next(self.newstream) for i in range(n)]
|
||||
#print d
|
||||
return d
|
||||
|
||||
|
||||
# Etherpoint all transform in one matrix, with warp !!
|
||||
# xyc : x y color
|
||||
def EtherPoint(self,xyc):
|
||||
|
||||
c = xyc[2]
|
||||
|
||||
#print("")
|
||||
#print("pygame point",[(xyc[0],xyc[1],xyc[2])])
|
||||
#gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser))))
|
||||
position = homographyp.apply(gstt.EDH[self.mylaser],np.array([(xyc[0],xyc[1])]))
|
||||
|
||||
#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], ((c >> 16) & 0xFF) << 8, ((c >> 8) & 0xFF) << 8, (c & 0xFF) << 8)
|
||||
|
||||
|
||||
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:])
|
||||
|
||||
r.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 __init__(self, mylaser, PL, port = 7765):
|
||||
"""Connect to the DAC over TCP."""
|
||||
socket.setdefaulttimeout(2)
|
||||
|
||||
self.mylaser = mylaser
|
||||
self.clientkey = r.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
|
||||
r.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(r.get(self.clientkey + str(self.mylaser)).decode('ascii'))
|
||||
if r.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(r.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.newstream = self.OnePoint()
|
||||
|
||||
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 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 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 prepare(self):
|
||||
self.conn.sendall("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(r.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(r.get(self.clientkey+str(self.mylaser)).decode('ascii'))
|
||||
#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(r.get('/EDH/'+str(self.mylaser)).decode('ascii')))
|
||||
# Back to user point list
|
||||
r.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(r.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
|
||||
r.set('/order/'+str(self.mylaser), 0)
|
||||
|
||||
# Client Key change
|
||||
if order == 5:
|
||||
print("Tracer", self.mylaser, "new clientkey")
|
||||
self.clientkey = r.get('/clientkey')
|
||||
# Back to user point list order
|
||||
r.set('/order/'+str(self.mylaser), 0)
|
||||
|
||||
# Intensity change
|
||||
if order == 6:
|
||||
self.intensity = int(r.get('/intensity/' + str(self.mylaser)).decode('ascii')) << 8
|
||||
print("Tracer" , self.mylaser, "new Intensity", self.intensity)
|
||||
gstt.intensity[self.mylaser] = self.intensity
|
||||
r.set('/order/'+str(self.mylaser), "0")
|
||||
|
||||
# kpps change
|
||||
if order == 7:
|
||||
gstt.kpps[self.mylaser] = int(r.get('/kpps/' + str(self.mylaser)).decode('ascii'))
|
||||
print("Tracer",self.mylaser,"new kpps", gstt.kpps[self.mylaser])
|
||||
self.update(0, gstt.kpps[self.mylaser])
|
||||
r.set('/order/'+str(self.mylaser), "0")
|
||||
|
||||
# color balance change
|
||||
if order == 8:
|
||||
gstt.red[self.mylaser] = int(r.get('/red/' + str(self.mylaser)).decode('ascii'))
|
||||
gstt.green[self.mylaser] = int(r.get('/green/' + str(self.mylaser)).decode('ascii'))
|
||||
gstt.blue[self.mylaser] = int(r.get('/blue/' + str(self.mylaser)).decode('ascii'))
|
||||
print("Tracer",self.mylaser,"new color balance", gstt.red[self.mylaser], gstt.green[self.mylaser], gstt.blue[self.mylaser])
|
||||
r.set('/order/'+str(self.mylaser), "0")
|
||||
|
||||
|
||||
|
||||
r.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)
|
||||
|
||||
r.set('/cap/'+str(self.mylaser), cap)
|
||||
|
||||
if cap < 100:
|
||||
time.sleep(0.001)
|
||||
cap += 150
|
||||
|
||||
# print("Writing %d points" % (cap, ))
|
||||
#t0 = time.time()
|
||||
#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
|
||||
|
||||
# not used in LJay.
|
||||
def find_dac():
|
||||
"""Listen for broadcast packets."""
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.bind(("0.0.0.0", 7654))
|
||||
|
||||
while True:
|
||||
data, addr = s.recvfrom(1024)
|
||||
bp = BroadcastPacket(data)
|
||||
|
||||
print(("Packet from %s: " % (addr, )))
|
||||
bp.dump()
|
||||
|
||||
|
||||
'''
|
||||
#Laser order bit 0 = 0
|
||||
if not order & (1 << (self.mylaser*2)):
|
||||
#print("laser",mylaser,"bit 0 : 0")
|
||||
|
||||
# Laser bit 0 = 0 and bit 1 = 0 : USER PL
|
||||
if not order & (1 << (1+self.mylaser*2)):
|
||||
#print("laser",mylaser,"bit 1 : 0")
|
||||
self.pl = ast.literal_eval(r.get('/pl/'+str(self.mylaser)))
|
||||
|
||||
else:
|
||||
# Laser bit 0 = 0 and bit 1 = 1 : New EDH
|
||||
#print("laser",mylaser,"bit 1 : 1" )
|
||||
print("Laser",self.mylaser,"new EDH ORDER in redis"
|
||||
gstt.EDH[self.mylaser]= np.array(ast.literal_eval(r.get('/EDH/'+str(self.mylaser))))
|
||||
# Back to USER PL
|
||||
order = r.get('/order')
|
||||
neworder = order & ~(1<< self.mylaser*2)
|
||||
neworder = neworder & ~(1<< 1+ self.mylaser*2)
|
||||
r.set('/order', str(neworder))
|
||||
else:
|
||||
|
||||
# Laser bit 0 = 1
|
||||
print("laser",mylaser,"bit 0 : 1")
|
||||
|
||||
# Laser bit 0 = 1 and bit 1 = 0 : Black PL
|
||||
if not order & (1 << (1+self.mylaser*2)):
|
||||
#print("laser",mylaser,"bit 1 : 0")
|
||||
self.pl = black_points
|
||||
|
||||
else:
|
||||
# Laser bit 0 = 1 and bit 1 = 1 : GRID PL
|
||||
#print("laser",mylaser,"bit 1 : 1" )
|
||||
self.pl = grid_points
|
||||
'''
|
3
www/build/README.md
Normal file
3
www/build/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
Source: https://github.com/auduno/clmtrackr
|
||||
License: MIT
|
||||
https://raw.githubusercontent.com/auduno/clmtrackr/dev/LICENSE.txt
|
15325
www/build/clmtrackr.js
Normal file
15325
www/build/clmtrackr.js
Normal file
File diff suppressed because one or more lines are too long
1
www/build/clmtrackr.min.js
vendored
Normal file
1
www/build/clmtrackr.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user