2019-08-06 01:08:54 +00:00
|
|
|
#!/usr/bin/python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
ArtNet/DMX Handler :
|
|
|
|
v0.7.0
|
|
|
|
|
|
|
|
|
|
|
|
by Sam Neurohack
|
|
|
|
from /team/laser
|
|
|
|
|
|
|
|
Artnet receving code from https://github.com/kongr45gpen/simply-artnet
|
|
|
|
Laser selection
|
|
|
|
one universe / laser
|
|
|
|
|
|
|
|
Plugin selection
|
|
|
|
banck change/scene/
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
|
2019-08-06 01:08:54 +00:00
|
|
|
import random
|
|
|
|
import pysimpledmx
|
|
|
|
from serial.tools import list_ports
|
|
|
|
import serial,time
|
|
|
|
from threading import Thread
|
|
|
|
import socket
|
|
|
|
import struct
|
|
|
|
import types
|
|
|
|
from sys import platform, version
|
|
|
|
import sys
|
|
|
|
import argparse, traceback
|
|
|
|
import os
|
2020-09-19 12:28:56 +00:00
|
|
|
import log
|
2019-08-06 01:08:54 +00:00
|
|
|
|
|
|
|
is_py2 = version[0] == '2'
|
2020-09-19 12:28:56 +00:00
|
|
|
|
2019-08-06 01:08:54 +00:00
|
|
|
if is_py2:
|
|
|
|
from OSC import OSCServer, OSCClient, OSCMessage
|
|
|
|
else:
|
|
|
|
from OSC3 import OSCServer, OSCClient, OSCMessage
|
|
|
|
|
|
|
|
ljpath = r'%s' % os.getcwd().replace('\\','/')
|
|
|
|
|
|
|
|
# import from shell
|
|
|
|
|
|
|
|
#sys.path.append('../../libs')
|
|
|
|
|
|
|
|
#import from LJ
|
|
|
|
sys.path.append(ljpath +'/libs/')
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
import lj23layers as lj
|
2019-08-06 01:08:54 +00:00
|
|
|
|
|
|
|
#
|
|
|
|
# Init
|
|
|
|
#
|
|
|
|
|
|
|
|
OSCinPort = 8032
|
|
|
|
|
|
|
|
|
|
|
|
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
|
|
|
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
|
|
|
|
sock.bind(('',6454))
|
|
|
|
|
|
|
|
dmxeq = {}
|
|
|
|
dmxstates = []
|
|
|
|
dmxinit = False
|
|
|
|
universe = []
|
|
|
|
|
|
|
|
|
|
|
|
for i in range(1,514):
|
|
|
|
dmxstates.append(-1)
|
|
|
|
|
|
|
|
|
|
|
|
print ("")
|
2020-09-19 12:28:56 +00:00
|
|
|
log.infog("Artnet v0.1")
|
2019-08-06 01:08:54 +00:00
|
|
|
print ("Arguments parsing if needed...")
|
|
|
|
argsparser = argparse.ArgumentParser(description="Artnet & DMX for LJ")
|
|
|
|
argsparser.add_argument("-u","--universe",help="Universe, not implemented (0 by default)",type=int)
|
|
|
|
argsparser.add_argument("-s","--subuniverse",help="Subniverse, not implemented (0 by default)",type=int)
|
|
|
|
argsparser.add_argument("-r","--redisIP",help="IP of the Redis server used by LJ (127.0.0.1 by default) ",type=str)
|
|
|
|
argsparser.add_argument("-m","--myIP",help="Local IP (127.0.0.1 by default) ",type=str)
|
|
|
|
argsparser.add_argument("-v","--verbose",help="Verbosity level (0 by default)",type=int)
|
|
|
|
|
|
|
|
args = argsparser.parse_args()
|
|
|
|
|
|
|
|
|
|
|
|
# Universe
|
|
|
|
if args.universe:
|
|
|
|
universenb = args.universe
|
|
|
|
else:
|
|
|
|
universenb = 0
|
|
|
|
|
|
|
|
# Universe
|
|
|
|
if args.subuniverse:
|
|
|
|
subuniversenb = args.subuniverse
|
|
|
|
else:
|
|
|
|
subuniversenb = 0
|
|
|
|
|
|
|
|
# Debug level
|
|
|
|
if args.verbose:
|
|
|
|
debug = args.verbose
|
|
|
|
else:
|
|
|
|
debug = 0
|
|
|
|
|
|
|
|
# Redis Computer IP
|
|
|
|
if args.redisIP != None:
|
|
|
|
redisIP = args.redisIP
|
|
|
|
else:
|
|
|
|
redisIP = '127.0.0.1'
|
|
|
|
|
|
|
|
# myIP
|
|
|
|
if args.myIP != None:
|
|
|
|
myIP = args.myIP
|
|
|
|
else:
|
|
|
|
myIP = '127.0.0.1'
|
|
|
|
|
|
|
|
r = lj.Config(redisIP, 255, "artnet")
|
|
|
|
|
|
|
|
lj.WebStatus("Artnet init...")
|
|
|
|
|
|
|
|
def lhex(h):
|
|
|
|
return ':'.join(x.encode('hex') for x in h)
|
|
|
|
|
|
|
|
|
|
|
|
def senddmx0():
|
|
|
|
for channel in range (1,512):
|
|
|
|
senddmx(channel,0)
|
|
|
|
|
|
|
|
def senddmx(channel, value):
|
|
|
|
|
|
|
|
print("Setting channel %d to %d" % (i,value))
|
|
|
|
#mydmx.setChannel((channel + 1 ), value, autorender=True)
|
|
|
|
# calling render() is better more reliable to actually sending data
|
|
|
|
# Some strange bug. Need to add one to required dmx channel is done automatically
|
|
|
|
mydmx.setChannel((channel ), value)
|
|
|
|
mydmx.render()
|
|
|
|
print("Sending DMX Channel : ", str(channel), " value : ", str(value))
|
|
|
|
|
|
|
|
def updateDmxValue(channel, val):
|
|
|
|
|
|
|
|
#
|
|
|
|
if dmxstates[channel] == -1:
|
|
|
|
dmxstates[channel] = val
|
|
|
|
|
|
|
|
# DMX UPDATE!!! WOW!!!
|
|
|
|
if dmxstates[channel] != val:
|
|
|
|
dmxstates[channel] = val
|
2020-09-19 12:28:56 +00:00
|
|
|
print("updating channel", channel, "with ", val)
|
2019-08-06 01:08:54 +00:00
|
|
|
if mydmx != False:
|
|
|
|
senddmx(channel, ord(val))
|
|
|
|
|
|
|
|
|
|
|
|
# Search for DMX devices
|
|
|
|
|
|
|
|
#ljnozoids.WebStatus("Available serial devices")
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
|
2019-08-06 01:08:54 +00:00
|
|
|
print("Available serial devices...")
|
|
|
|
ports = list(list_ports.comports())
|
|
|
|
|
|
|
|
portnumber = 0
|
|
|
|
|
|
|
|
# Get all serial ports names
|
|
|
|
for i, p in enumerate(ports):
|
|
|
|
|
|
|
|
print(i, ":", p)
|
|
|
|
|
|
|
|
if p[0]== "/dev/ttyUSB0":
|
|
|
|
portname[portnumber] = p[0]
|
|
|
|
portnumber += 1
|
|
|
|
|
|
|
|
|
|
|
|
if platform == 'darwin' and p[1].find("DMX USB PRO") != -1:
|
|
|
|
portname[portnumber] = p[0]
|
|
|
|
portnumber += 1
|
|
|
|
|
|
|
|
|
|
|
|
# ljnozoids.WebStatus("Found " + str(portnumber) +" Nozoids.")
|
|
|
|
|
|
|
|
print("Found", portnumber, "DMX devices")
|
|
|
|
|
|
|
|
|
|
|
|
if portnumber > 0:
|
|
|
|
|
|
|
|
print("with serial names", portname)
|
|
|
|
mydmx = pysimpledmx.DMXConnection(gstt.serdmx[0])
|
|
|
|
senddmx0()
|
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
# Send a random value to channel 1
|
|
|
|
vrand=random.randint(0,255)
|
|
|
|
senddmx(1,vrand)
|
|
|
|
|
|
|
|
else:
|
|
|
|
mydmx = False
|
|
|
|
print("No DMX found, Art-Net receiver only.")
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# OSC
|
|
|
|
#
|
|
|
|
|
|
|
|
oscserver = OSCServer( (myIP, OSCinPort) )
|
|
|
|
oscserver.timeout = 0
|
|
|
|
#oscrun = True
|
|
|
|
|
|
|
|
# this method of reporting timeouts only works by convention
|
|
|
|
# that before calling handle_request() field .timed_out is
|
|
|
|
# set to False
|
|
|
|
def handle_timeout(self):
|
|
|
|
self.timed_out = True
|
|
|
|
|
|
|
|
# funny python's way to add a method to an instance of a class
|
|
|
|
oscserver.handle_timeout = types.MethodType(handle_timeout, oscserver)
|
|
|
|
|
|
|
|
|
|
|
|
# default handler
|
|
|
|
def OSChandler(path, tags, args, source):
|
|
|
|
|
|
|
|
oscaddress = ''.join(path.split("/"))
|
2020-09-19 12:28:56 +00:00
|
|
|
print(("Default OSC Handler : msg from Client : " + str(source[0]),))
|
|
|
|
print(("OSC address", path, "with",))
|
2019-08-06 01:08:54 +00:00
|
|
|
if len(args) > 0:
|
2020-09-19 12:28:56 +00:00
|
|
|
print(("args", args))
|
2019-08-06 01:08:54 +00:00
|
|
|
else:
|
|
|
|
print("noargs")
|
|
|
|
#oscIPout = str(source[0])
|
|
|
|
#osclient.connect((oscIPout, oscPORTout))
|
|
|
|
|
|
|
|
|
|
|
|
# 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()
|
|
|
|
|
|
|
|
|
|
|
|
# Stop osc server
|
|
|
|
def OSCstop():
|
|
|
|
|
|
|
|
oscrun = False
|
|
|
|
oscserver.close()
|
|
|
|
|
|
|
|
|
|
|
|
# /sendmx channel value
|
|
|
|
def OSCsendmx(path, tags, args, source):
|
|
|
|
|
|
|
|
channel = args[0]
|
|
|
|
val = args[1]
|
|
|
|
updateDmxValue(channel, val)
|
|
|
|
|
|
|
|
|
|
|
|
lj.addOSCdefaults(oscserver)
|
|
|
|
lj.SendLJ("/pong", "artnet")
|
|
|
|
lj.WebStatus("Artnet Running...")
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
log.infog("Artnet running...")
|
|
|
|
print()
|
|
|
|
|
2019-08-06 01:08:54 +00:00
|
|
|
oscserver.addMsgHandler( "/sendmx", OSCsendmx )
|
|
|
|
|
|
|
|
#
|
|
|
|
# Running...
|
|
|
|
#
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
'''
|
2019-08-06 01:08:54 +00:00
|
|
|
print ("Starting, use Ctrl+C to stop")
|
|
|
|
print (lj.oscrun)
|
2020-09-19 12:28:56 +00:00
|
|
|
'''
|
2019-08-06 01:08:54 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
while lj.oscrun:
|
|
|
|
|
|
|
|
data = sock.recv(10240)
|
|
|
|
if len(data) < 20:
|
|
|
|
continue
|
|
|
|
|
|
|
|
if data[0:7] != "Art-Net" or data[7] != "\0":
|
|
|
|
print("artnet package")
|
|
|
|
#lj.WebStatus("Artnet package")
|
|
|
|
continue
|
|
|
|
OSCframe()
|
|
|
|
|
|
|
|
if ord(data[8]) != 0x00 or ord(data[9]) != 0x50:
|
|
|
|
print("OpDmx")
|
|
|
|
continue
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
print(("oscrun", lj.oscrun))
|
2019-08-06 01:08:54 +00:00
|
|
|
protverhi = ord(data[10])
|
|
|
|
protverlo = ord(data[11])
|
|
|
|
sequence = ord(data[12])
|
|
|
|
physical = ord(data[13])
|
|
|
|
subuni = ord(data[14])
|
|
|
|
net = ord(data[15])
|
|
|
|
lengthhi = ord(data[16])
|
|
|
|
length = ord(data[17])
|
|
|
|
dmx = data[18:]
|
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
print((data[0:7], "version :",lhex(data[10])+lhex(data[11]), "sequence :", sequence, "physical", physical, "subuni",subuni,"net", net))
|
2019-08-06 01:08:54 +00:00
|
|
|
|
2020-09-19 12:28:56 +00:00
|
|
|
for i in range(0,510):
|
2019-08-06 01:08:54 +00:00
|
|
|
updateDmxValue(i+1,dmx[i])
|
|
|
|
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
traceback.print_exc()
|
|
|
|
|
|
|
|
finally:
|
|
|
|
|
|
|
|
lj.ClosePlugin()
|
|
|
|
sock.close()
|
|
|
|
OSCstop()
|
|
|
|
'''
|
|
|
|
import sys, socket, math, time
|
|
|
|
from ctypes import *
|
|
|
|
|
|
|
|
class Artnet:
|
|
|
|
class ArtNetDMXOut(LittleEndianStructure):
|
|
|
|
PORT = 0x1936
|
|
|
|
_fields_ = [("id", c_char * 8),
|
|
|
|
("opcode", c_ushort),
|
|
|
|
("protverh", c_ubyte),
|
|
|
|
("protver", c_ubyte),
|
|
|
|
("sequence", c_ubyte),
|
|
|
|
("physical", c_ubyte),
|
|
|
|
("universe", c_ushort),
|
|
|
|
("lengthhi", c_ubyte),
|
|
|
|
("length", c_ubyte),
|
|
|
|
("payload", c_ubyte * 512)]
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.id = b"Art-Net"
|
|
|
|
self.opcode = 0x5000
|
|
|
|
self.protver = 14
|
|
|
|
self.universe = 0
|
|
|
|
self.lengthhi = 2
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.artnet = Artnet.ArtNetDMXOut()
|
|
|
|
self.S = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
|
|
|
|
for i in range(512):
|
|
|
|
self.artnet.payload[i] = 0
|
|
|
|
|
|
|
|
def send(self,data,IP,port):
|
|
|
|
# send(送るデータ,IPアドレス,ポート番号)
|
|
|
|
self.artnet.universe = port
|
|
|
|
for i in range(512):
|
|
|
|
if(i < len(data)):
|
|
|
|
self.artnet.payload[i] = data[i]
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
self.S.sendto(self.artnet,(IP,Artnet.ArtNetDMXOut.PORT))
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
artnet = Artnet()
|
|
|
|
data = [0] * 512
|
|
|
|
for i in range(150):
|
|
|
|
data[i*3+0] = 0
|
|
|
|
data[i*3+1] = 0
|
|
|
|
data[i*3+2] = 0
|
|
|
|
artnet.send(data,"133.15.42.111",5)
|
|
|
|
'''
|