diff --git a/README.md b/README.md index 4c0b6e2..8d7a061 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,8 @@ LJ is in dev : versions in this repository will always be core functionnal : acc # Install # -With Linux Buster, in LJ directory, type in a terminal window : + +- Linux Buster : in LJ directory, type in a terminal window : cd server ./install.sh @@ -82,22 +83,23 @@ cd server Server directory also contains config files for optionnal nginx, supervisorctl and syncthing. -For OS X, you need brew already installed, then : +- OS X : you need brew already installed, then : brew update brew upgrade brew install redis +cd server type all install.sh commands beginning line 4. An OS X install script soon !! -For Linux and OS X : + +- KVM : +an ISO is available here : https://www.tmplab.org/wp-content/lazer-iso.zip + +- Postinstall for all : You probably want redis bound to all network interfaces : comment the bind line in /etc/redis/redis.conf and restart it. -WebUI pages needs to know the LJ IP address. So you need to change the line wwwIP = "192.168.2.43" in updateUI.py then run python updateUI.py - -Using the same idea check all ip address in LJ.conf. - -There is a nice websocket debug tool : websocat. +You probably also want to run the configure script to enter your etherdreams IPs,... python3 configure.py @@ -342,20 +344,18 @@ See links section for great etherdream managing tools. About hardware setup, especially if you have several lasers : ILDA cables are insanely expensive. For each DAC, buy a very small ILDA cable and RJ 45 cable, all DAC goes to a local switch and only one long cable to your You may also consider the Power Over Ethernet 'POE' option. Buy a POE splitter and connect everything to the ether dream fixed near your laser. You can have then a simple and very long network cable and use a Power Over Ethernet injector or switch close to the driving computer. Beware some vendors use 24V POE Injector : POE injectors and splitters must match. +# +# Ethertools directory +# + +2 useful and always working tools from j4cdac github repository : sitter and talk +- Sitter will display all real etherdreams available on the network and their state (playing, idle,...). python sitter.py or use the compiled version (for macOS). +- Talk : will draw a 4 colors square. python3 talk3.py # # Links # -Tools : - -Display all connected etherdreams on the network : ![sitter](https://github.com/j4cbo/j4cDAC/tree/master/tools/sitter) -python sitter.py - -Draw simple square : ![talk](https://github.com/j4cbo/j4cDAC/tree/master/tools/tester) - -python talk.py - Generic : @@ -366,6 +366,8 @@ Generic : ![Laser Faq](https://www.repairfaq.org/sam/lasersam.htm) +There is a nice websocket debug tool : websocat. + # # LJ commands reference # diff --git a/ethertools/dac3.py b/ethertools/dac3.py new file mode 100755 index 0000000..325672b --- /dev/null +++ b/ethertools/dac3.py @@ -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 . + +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(" 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(" + + + + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + + CFBundleTypeOSTypes + + **** + fold + disk + + CFBundleTypeRole + Viewer + + + CFBundleExecutable + sitter + CFBundleIconFile + PythonApplet.icns + CFBundleIdentifier + sitter + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + sitter + CFBundlePackageType + APPL + CFBundleSignature + ???? + + diff --git a/ethertools/sitter.app/Contents/MacOS/Python b/ethertools/sitter.app/Contents/MacOS/Python new file mode 100755 index 0000000..1cfc9df Binary files /dev/null and b/ethertools/sitter.app/Contents/MacOS/Python differ diff --git a/ethertools/sitter.app/Contents/MacOS/sitter b/ethertools/sitter.app/Contents/MacOS/sitter new file mode 100755 index 0000000..56c2a1d --- /dev/null +++ b/ethertools/sitter.app/Contents/MacOS/sitter @@ -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) diff --git a/ethertools/sitter.app/Contents/PkgInfo b/ethertools/sitter.app/Contents/PkgInfo new file mode 100755 index 0000000..bd04210 --- /dev/null +++ b/ethertools/sitter.app/Contents/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/ethertools/sitter.app/Contents/Resources/PythonApplet.icns b/ethertools/sitter.app/Contents/Resources/PythonApplet.icns new file mode 100755 index 0000000..c8aad9f Binary files /dev/null and b/ethertools/sitter.app/Contents/Resources/PythonApplet.icns differ diff --git a/ethertools/sitter.app/Contents/Resources/__argvemulator_sitter.py b/ethertools/sitter.app/Contents/Resources/__argvemulator_sitter.py new file mode 100755 index 0000000..4f0c146 --- /dev/null +++ b/ethertools/sitter.app/Contents/Resources/__argvemulator_sitter.py @@ -0,0 +1,4 @@ +import argvemulator, os + +argvemulator.ArgvCollector().mainloop() +execfile(os.path.join(os.path.split(__file__)[0], "sitter.py")) diff --git a/ethertools/sitter.app/Contents/Resources/sitter.py b/ethertools/sitter.app/Contents/Resources/sitter.py new file mode 100755 index 0000000..a1aad5e --- /dev/null +++ b/ethertools/sitter.app/Contents/Resources/sitter.py @@ -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 . + +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(" 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(">", 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() diff --git a/ethertools/sitter.py b/ethertools/sitter.py new file mode 100755 index 0000000..677971b --- /dev/null +++ b/ethertools/sitter.py @@ -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 . + +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(" 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(">", 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() diff --git a/ethertools/talk3.py b/ethertools/talk3.py new file mode 100755 index 0000000..183e613 --- /dev/null +++ b/ethertools/talk3.py @@ -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 . + +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()) diff --git a/ethertools/watch.py b/ethertools/watch.py new file mode 100755 index 0000000..9fff745 --- /dev/null +++ b/ethertools/watch.py @@ -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 . + +import dac + +dac.find_dac() diff --git a/updateUI.py b/updateUI.py index 969378e..e2f2a90 100644 --- a/updateUI.py +++ b/updateUI.py @@ -14,7 +14,7 @@ ljpath = r'%s' % os.getcwd().replace('\\','/') python2 = (2, 6) <= sys.version_info < (3, 0) -def Updatewww(file_name): +def Updatepage(file_name): print("updating", file_name) f=open(file_name,"r+") @@ -53,10 +53,14 @@ def Updatewww(file_name): o.close() #now the modification is done in the file -print("Updating www files...") -Updatewww(ljpath+"/www/LJ.js") -Updatewww(ljpath+"/www/trckr/trckrcam1.html") -Updatewww(ljpath+"/www/simu.html") -Updatewww(ljpath+"/www/align.html") -Updatewww(ljpath+"/www/auralls.html") -Updatewww(ljpath+"/www/index.html") +def www(wwwip): + global wwwIP + + wwwIP = wwwip + print("Updating www files to use", wwwIP) + Updatepage(ljpath+"/www/LJ.js") + Updatepage(ljpath+"/www/trckr/trckrcam1.html") + Updatepage(ljpath+"/www/simu.html") + Updatepage(ljpath+"/www/align.html") + Updatepage(ljpath+"/www/auralls.html") + Updatepage(ljpath+"/www/index.html")