Compare commits
	
		
			14 Commits
		
	
	
		
			bb19158aa0
			...
			dthomas-db
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0299b3877d | |||
| 
						 | 
					fc6baa8826 | ||
| 20a970025d | |||
| 0d597a1866 | |||
| 329c3e5108 | |||
| 13b1c87b94 | |||
| 2a9a6c1576 | |||
| c3fc682123 | |||
| 0bbfa2d49c | |||
| 9ac8f95982 | |||
| 3371f4031c | |||
| d30831fc55 | |||
| 1659d38726 | |||
| 
						
						
							
						
						75fad013c0
	
				 | 
					
					
						
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,9 @@
 | 
			
		||||
venv
 | 
			
		||||
__pycache__
 | 
			
		||||
cables
 | 
			
		||||
cables
 | 
			
		||||
.vscode
 | 
			
		||||
output.log
 | 
			
		||||
*.webm
 | 
			
		||||
output.mp4
 | 
			
		||||
output.log
 | 
			
		||||
cables-sample.zip
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								badapple.mp4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								badapple.mp4
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										166
									
								
								banner_ivu_export.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										166
									
								
								banner_ivu_export.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import socket
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
from time import sleep
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
from: https://github.com/MoisesBrito31/ve_data_log/blob/main/serverContagem/VE/drive.py
 | 
			
		||||
(no license)
 | 
			
		||||
 | 
			
		||||
(partially) adapted to English language & iVu camera instead of classic VE by Cole Deck
 | 
			
		||||
 | 
			
		||||
"""
 | 
			
		||||
 | 
			
		||||
def gravaLog(ip="1",tipo="Evento", msg="", file="log_imagem.txt"):
 | 
			
		||||
    # removed full logging
 | 
			
		||||
    fprint(msg)
 | 
			
		||||
 | 
			
		||||
class DriveImg():
 | 
			
		||||
    HEADERSIZE = 100
 | 
			
		||||
    ip = "192.168.0.1"
 | 
			
		||||
    port = 32200
 | 
			
		||||
    onLine = False
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ip, port, pasta = "media/"):
 | 
			
		||||
        self.pasta = pasta
 | 
			
		||||
        self.ip=ip
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 | 
			
		||||
        self.trans.settimeout(5)
 | 
			
		||||
        fprint("Trying to connect...")
 | 
			
		||||
        try:
 | 
			
		||||
            self.trans.connect((self.ip,self.port))
 | 
			
		||||
            self.onLine = True
 | 
			
		||||
            fprint("Camera Online")
 | 
			
		||||
            #self.trans.close()
 | 
			
		||||
        except:
 | 
			
		||||
            self.onLine = False 
 | 
			
		||||
            fprint("Offline")
 | 
			
		||||
 | 
			
		||||
    def read_img(self):
 | 
			
		||||
        resposta = 'Falha'
 | 
			
		||||
        try:
 | 
			
		||||
            if not self.onLine:
 | 
			
		||||
                #print(f'tentando Conectar camera {self.ip}...')
 | 
			
		||||
                gravaLog(ip=self.ip,msg=f'Trying to connect...')
 | 
			
		||||
                sleep(2)
 | 
			
		||||
                try:
 | 
			
		||||
                    self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 | 
			
		||||
                    self.trans.connect((self.ip,self.PORT))
 | 
			
		||||
                    self.onLine = True
 | 
			
		||||
                    gravaLog(ip=self.ip,msg=f'Connection established.')
 | 
			
		||||
                except:
 | 
			
		||||
                    self.onLine = False
 | 
			
		||||
                    self.trans.close()
 | 
			
		||||
                    return resposta
 | 
			
		||||
            ret = self.trans.recv(64)
 | 
			
		||||
            try:
 | 
			
		||||
                valida = str(ret[0:15].decode('UTF-8'))
 | 
			
		||||
                #print(valida)
 | 
			
		||||
                if valida.find("TC IMAGE")<0:
 | 
			
		||||
                    self.onLine = False
 | 
			
		||||
                    self.trans.close()
 | 
			
		||||
                    sleep(2)
 | 
			
		||||
                    gravaLog(ip=self.ip,tipo="Falha",msg=f'Unable to find TC IMAGE bookmark')
 | 
			
		||||
                    return "Error"
 | 
			
		||||
            except Exception as ex:
 | 
			
		||||
                self.onLine = False
 | 
			
		||||
                self.trans.close()
 | 
			
		||||
                sleep(2)
 | 
			
		||||
                gravaLog(ip=self.ip,tipo="Falha",msg=f'Error - {str(ex)}')
 | 
			
		||||
                return "Error"
 | 
			
		||||
            if ret:
 | 
			
		||||
                frame = int.from_bytes(ret[24:27],"little")
 | 
			
		||||
                isJpeg = int.from_bytes(ret[32:33],"little")
 | 
			
		||||
                img_size = int.from_bytes(ret[20:23],"little")
 | 
			
		||||
                data = self.trans.recv(5000)
 | 
			
		||||
                while img_size>len(data) and ret:
 | 
			
		||||
                    ret = self.trans.recv(10000)
 | 
			
		||||
                    if ret:
 | 
			
		||||
                        data = data+ret
 | 
			
		||||
                        #print(f'{len(ret)}b dados recebidos, total de: {len(data)+64}b')
 | 
			
		||||
                    else:
 | 
			
		||||
                        gravaLog(ip=self.ip,tipo="Falha",msg="Unable to recieve the image")
 | 
			
		||||
                        self.onLine = False
 | 
			
		||||
                        return "Unable to recieve the image"
 | 
			
		||||
                hoje = datetime.now()
 | 
			
		||||
                idcam = self.ip.split('.')
 | 
			
		||||
                """try:
 | 
			
		||||
                    nomeFile = f'{hoje.day}{hoje.month}{hoje.year}-{idcam[3]}-{frame}'
 | 
			
		||||
                    if isJpeg==1:
 | 
			
		||||
                        file = open(f'{self.pasta}{nomeFile}.jpg','wb')
 | 
			
		||||
                        nomeFile = f'{nomeFile}.jpg'
 | 
			
		||||
                    else:
 | 
			
		||||
                        file = open(f'{self.pasta}{nomeFile}.bmp','wb')
 | 
			
		||||
                        nomeFile = f'{nomeFile}.bmp'
 | 
			
		||||
                    file.write(data)
 | 
			
		||||
                    file.close()
 | 
			
		||||
                except Exception as ex:
 | 
			
		||||
                    sleep(2)
 | 
			
		||||
                    gravaLog(ip=self.ip,tipo="Falha",msg=f'Error - {str(ex)}')
 | 
			
		||||
                    return "Falha" 
 | 
			
		||||
                    """
 | 
			
		||||
                if isJpeg==1:
 | 
			
		||||
                    return "jpeg",data
 | 
			
		||||
                else:
 | 
			
		||||
                    return "bmp",data
 | 
			
		||||
                
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            gravaLog(ip=self.ip,tipo="Falha Generica",msg=f'Error - {str(ex)}')
 | 
			
		||||
            #print(f'erro {str(ex)}')
 | 
			
		||||
            self.onLine = False
 | 
			
		||||
            self.trans.close()
 | 
			
		||||
            sleep(2)
 | 
			
		||||
            return resposta
 | 
			
		||||
 | 
			
		||||
class DriveData():
 | 
			
		||||
    HEADERSIZE = 100
 | 
			
		||||
    ip = "192.168.0.1"
 | 
			
		||||
    port = 32100
 | 
			
		||||
    onLine = False
 | 
			
		||||
 | 
			
		||||
    def __init__(self, ip, port):
 | 
			
		||||
        gravaLog(ip=self.ip,msg=f'iniciou drive',file="log_data.txt")
 | 
			
		||||
        self.ip=ip
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 | 
			
		||||
        try:
 | 
			
		||||
            self.trans.connect((self.ip,self.port))
 | 
			
		||||
            self.onLine = True
 | 
			
		||||
        except:
 | 
			
		||||
            self.onLine = False 
 | 
			
		||||
 | 
			
		||||
    def read_data(self):
 | 
			
		||||
        resposta = 'falha'
 | 
			
		||||
        try:
 | 
			
		||||
            if not self.onLine:
 | 
			
		||||
                #print(f'tentando Conectar...\n')
 | 
			
		||||
                gravaLog(ip=self.ip,msg=f'tentando Conectar...',file="log_data.txt")
 | 
			
		||||
                sleep(2)
 | 
			
		||||
                try:
 | 
			
		||||
                    self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 | 
			
		||||
                    self.trans.connect((self.ip,self.PORT))
 | 
			
		||||
                    self.onLine = True
 | 
			
		||||
                    gravaLog(ip=self.ip,msg=f'Conexão restabelecida...',file="log_data.txt")
 | 
			
		||||
                except:
 | 
			
		||||
                    self.onLine = False
 | 
			
		||||
                    return resposta
 | 
			
		||||
            resposta = self.trans.recv(self.HEADERSIZE).decode("utf-8")
 | 
			
		||||
            resposta = str(resposta).split(',')
 | 
			
		||||
            return resposta
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            self.onLine = False 
 | 
			
		||||
            gravaLog(ip=self.ip,tipo="Falha Generica",msg=f'erro {str(ex)}',file="log_data.txt")
 | 
			
		||||
            sleep(2)
 | 
			
		||||
            return resposta
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    test = DriveImg("192.168.1.125", 32200)
 | 
			
		||||
    x = 0
 | 
			
		||||
    while x < 100:
 | 
			
		||||
        x=x+1
 | 
			
		||||
        imgtype, img = test.read_img()
 | 
			
		||||
							
								
								
									
										425
									
								
								config.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								config.yml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,425 @@
 | 
			
		||||
core:
 | 
			
		||||
  mode: linuxserver
 | 
			
		||||
  serverip: 172.26.178.114
 | 
			
		||||
  clientip: 172.26.176.1
 | 
			
		||||
  server: Hyper-Vd
 | 
			
		||||
 | 
			
		||||
arm:
 | 
			
		||||
  ip: 192.168.1.145
 | 
			
		||||
 | 
			
		||||
#cable_map:
 | 
			
		||||
cameras:
 | 
			
		||||
  banner:
 | 
			
		||||
    ip: 192.168.1.125
 | 
			
		||||
    port: 32200
 | 
			
		||||
 | 
			
		||||
led: 
 | 
			
		||||
  fps: 90
 | 
			
		||||
  timeout: 0
 | 
			
		||||
  controllers:
 | 
			
		||||
    - universe: 9
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 0
 | 
			
		||||
      ledend: 143
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 3
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 144
 | 
			
		||||
      ledend: 287
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 2
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 288
 | 
			
		||||
      ledend: 431
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 1
 | 
			
		||||
      ip: 192.168.68.130
 | 
			
		||||
      ledstart: 432
 | 
			
		||||
      ledend: 575
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 4
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 576
 | 
			
		||||
      ledend: 719
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 5
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 720
 | 
			
		||||
      ledend: 863
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 6
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 864
 | 
			
		||||
      ledend: 1007
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 7
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 1008
 | 
			
		||||
      ledend: 1151
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 8
 | 
			
		||||
      ip: 192.168.68.131
 | 
			
		||||
      ledstart: 1152
 | 
			
		||||
      ledend: 1295
 | 
			
		||||
      mode: rgb
 | 
			
		||||
    - universe: 0
 | 
			
		||||
      ip: 192.168.68.130
 | 
			
		||||
      ledstart: 1296
 | 
			
		||||
      ledend: 1365
 | 
			
		||||
      mode: rgbw
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
  map:
 | 
			
		||||
    # total for 54x rings: 1296 LEDs (0-1295), 24 ea
 | 
			
		||||
    # controller 1
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 0
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, 304.8]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 24
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.991, 266.7]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 48
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, 228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 72
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, 190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 96
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-263.965, 152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 120
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-263.965, 76.2]
 | 
			
		||||
 | 
			
		||||
    # controller 2
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 144
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, 228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 168
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.991, 190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 192
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, 152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 216
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, 114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 240
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, 38.1]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 264
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-263.965, 0]
 | 
			
		||||
 | 
			
		||||
    # controller 3
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 288
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, 152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 312
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.936, 114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 336
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, 76.2]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 360
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, 0]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 384
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, -38.1]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 408
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-263.965, -76.2]
 | 
			
		||||
 | 
			
		||||
    # controller 4
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 432
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, 76.2]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 456
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, 152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 480
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, 228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 504
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, 266.7]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 528
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, 190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 552
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, 114.3]
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    # controller 5
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 576
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, 0]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 600
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, 38.1]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 624
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, 114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 648
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, 190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 672
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [263.965, 152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 696
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [263.965, 76.2]
 | 
			
		||||
 | 
			
		||||
    # controller 6
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 720
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, -76.2]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 744
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, -38.1]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 768
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [263.965, 0]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 792
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [263.965, -76.2]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 816
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [263.965, -152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 840
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, -114.3]
 | 
			
		||||
 | 
			
		||||
    # controller 7
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 864
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, -114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 888
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, -152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 912
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.991, -114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 936
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, -76.2]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 960
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, -114.3]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 984
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, -152.4]
 | 
			
		||||
 | 
			
		||||
    # controller 8
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1008
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, -228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1032
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.991, -190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1056
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-65.991, -266.7]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1080
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-131.982, -228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1104
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-197.973, -190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1128
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [-263.965, -152.4]
 | 
			
		||||
 | 
			
		||||
    # controller 9
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1152
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [0, -304.8]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1176
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, -266.7]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1200
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, -228.6]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1224
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [197.973, -190.5]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1248
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [131.982, -152.4]
 | 
			
		||||
    - type: circle
 | 
			
		||||
      start: 1272
 | 
			
		||||
      size: 24
 | 
			
		||||
      diameter: 63.5
 | 
			
		||||
      angle: 0
 | 
			
		||||
      pos: [65.991, -190.5]
 | 
			
		||||
 | 
			
		||||
    # Strips
 | 
			
		||||
    - type: strip
 | 
			
		||||
      start: 1296
 | 
			
		||||
      size: 70
 | 
			
		||||
      length: 600
 | 
			
		||||
      angle: 270 # down
 | 
			
		||||
      pos: [300, 300]
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
							
								
								
									
										14
									
								
								database.py
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								database.py
									
									
									
									
									
								
							@@ -60,13 +60,18 @@ class DBConnector:
 | 
			
		||||
        return rows
 | 
			
		||||
 | 
			
		||||
    def _column_parity(self, columns: list[str] | set[str]) -> set[str]:
 | 
			
		||||
        """If the listed columns are not in the database, add them."""
 | 
			
		||||
        """If the listed columns are not in the database, add them.
 | 
			
		||||
 | 
			
		||||
        :param columns: The columns we expect are in the database.
 | 
			
		||||
        :return: The list of columns in the database after querying."""
 | 
			
		||||
        cols = set(columns)
 | 
			
		||||
        existing = self._get_cols()
 | 
			
		||||
        needs = cols.difference(existing.intersection(cols))
 | 
			
		||||
        query = f"ALTER TABLE {DB_TABLE} {', '.join([f'ADD COLUMN {c}' for c in needs])}"
 | 
			
		||||
        self._query(query)
 | 
			
		||||
        return self._get_cols()
 | 
			
		||||
        if len(needs) > 0:
 | 
			
		||||
            query = f"ALTER TABLE {DB_TABLE} {', '.join([f'ADD COLUMN {c}' for c in needs])}"
 | 
			
		||||
            self._query(query)
 | 
			
		||||
            existing = self._get_cols()
 | 
			
		||||
        return existing
 | 
			
		||||
 | 
			
		||||
    def _query(self, sql) -> list[dict]:
 | 
			
		||||
        """Basic function for running queries.
 | 
			
		||||
@@ -114,6 +119,7 @@ class DBConnector:
 | 
			
		||||
 | 
			
		||||
        :param kwargs: Values to write for each database; specify each column separately!
 | 
			
		||||
        :returns: The row you just added."""
 | 
			
		||||
        self._column_parity(set(kwargs.keys()))
 | 
			
		||||
        values = []
 | 
			
		||||
        for val in kwargs.keys():
 | 
			
		||||
            values.append(kwargs[val])
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								download-vid.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										11
									
								
								download-vid.sh
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#!/bin/bash
 | 
			
		||||
 | 
			
		||||
set -euxo pipefail 
 | 
			
		||||
 | 
			
		||||
mkdir -p tmpdl
 | 
			
		||||
cd tmpdl
 | 
			
		||||
yt-dlp $1
 | 
			
		||||
 | 
			
		||||
ffmpeg -i * -vf "scale=800x480" -r $2 ../output.mp4
 | 
			
		||||
 | 
			
		||||
rm -rf tmpdl
 | 
			
		||||
							
								
								
									
										52
									
								
								get_specs.py
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								get_specs.py
									
									
									
									
									
								
							@@ -8,6 +8,7 @@ import requests
 | 
			
		||||
#import time
 | 
			
		||||
import json
 | 
			
		||||
import subprocess
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
bartext = ""
 | 
			
		||||
failed = []
 | 
			
		||||
@@ -33,16 +34,16 @@ def query_search(partnum):
 | 
			
		||||
    search_url = "https://www.belden.com/coveo/rest/search"
 | 
			
		||||
    search_data ='{ "q": "' + str(partnum) + '", "sortCriteria": "relevancy", "numberOfResults": "250", "sortCriteria": "@catalogitemwebdisplaypriority ascending", "searchHub": "products-only-search", "pipeline": "Site Search", "maximumAge": "900000", "tab": "products-search", "locale": "en" }'
 | 
			
		||||
    #"aq": "", "cq": "((@z95xlanguage==en) (@z95xlatestversion==1) (@source==\\"Coveo_web_index - rg-nc-prod-sitecore-prod\\")) OR (@source==(\\"website_001002_catalog_index-rg-nc-prod-sitecore-prod\\",\\"website_001002_Category_index-rg-nc-prod-sitecore-prod\\"))", "firstResult": "0", "categoryFacets": "[{\\"field\\":\\"@catalogitemcategories\\",\\"path\\":[],\\"injectionDepth\\":1000,\\"maximumNumberOfValues\\":6,\\"delimitingCharacter\\":\\"|\\"}]", "facetOptions": "{}", "groupBy": "" }'
 | 
			
		||||
    #print(search_data)
 | 
			
		||||
    print(json.loads(search_data))
 | 
			
		||||
    #fprint(search_data)
 | 
			
		||||
    fprint(json.loads(search_data))
 | 
			
		||||
    #search_data = '{ "q": "' + str(partnum) + '" }'
 | 
			
		||||
    print(search_data)
 | 
			
		||||
    fprint(search_data)
 | 
			
		||||
    headers = headers = {
 | 
			
		||||
        'Authorization': f'Bearer {token}',
 | 
			
		||||
        'Content-Type': 'application/json'
 | 
			
		||||
    }
 | 
			
		||||
    with requests.post(search_url, headers=headers, data=search_data) as r:
 | 
			
		||||
        print(r.text)"""
 | 
			
		||||
        fprint(r.text)"""
 | 
			
		||||
    
 | 
			
		||||
    # TODO: Reimplement in python
 | 
			
		||||
    # Bash script uses some crazy json formatting that I could not figure out
 | 
			
		||||
@@ -52,7 +53,7 @@ def query_search(partnum):
 | 
			
		||||
    command = ["./query-search.sh", partnum]
 | 
			
		||||
    result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
 | 
			
		||||
    if result.returncode != 0: # error
 | 
			
		||||
        print("No results found in search database for " + partnum + ". No hi-res part image available.", result.stderr)
 | 
			
		||||
        fprint("No results found in search database for " + partnum + ". No hi-res part image available.", result.stderr)
 | 
			
		||||
        return False
 | 
			
		||||
    else:
 | 
			
		||||
        data_out = json.loads(result.stdout)
 | 
			
		||||
@@ -72,7 +73,7 @@ def get_multi(partnums):
 | 
			
		||||
 | 
			
		||||
            sanitized_name = partnum.replace(" ", "")
 | 
			
		||||
            url = "https://catalog.belden.com/techdata/EN/" + sanitized_name + "_techdata.pdf"
 | 
			
		||||
            #print(url)
 | 
			
		||||
            #fprint(url)
 | 
			
		||||
            try:
 | 
			
		||||
                with requests.get(url, stream=True) as r:
 | 
			
		||||
                    #r.raise_for_status()
 | 
			
		||||
@@ -89,10 +90,10 @@ def get_multi(partnums):
 | 
			
		||||
                            bartext = bartext + "."
 | 
			
		||||
                            bar.text = bartext
 | 
			
		||||
                            f.write(chunk)
 | 
			
		||||
                #print("")
 | 
			
		||||
                #fprint("")
 | 
			
		||||
                return output_dir + "/datasheet.pdf"
 | 
			
		||||
            except KeyboardInterrupt:
 | 
			
		||||
                print("Quitting!")
 | 
			
		||||
                fprint("Quitting!")
 | 
			
		||||
                os.remove(output_dir + "/datasheet.pdf")
 | 
			
		||||
                sys.exit()
 | 
			
		||||
 | 
			
		||||
@@ -100,7 +101,7 @@ def get_multi(partnums):
 | 
			
		||||
        def _download_datasheet(url, output_dir): # Download datasheet with known URL
 | 
			
		||||
            global bartext
 | 
			
		||||
 | 
			
		||||
            #print(url)
 | 
			
		||||
            #fprint(url)
 | 
			
		||||
            try:
 | 
			
		||||
                with requests.get(url, stream=True) as r:
 | 
			
		||||
                    #r.raise_for_status()
 | 
			
		||||
@@ -117,10 +118,10 @@ def get_multi(partnums):
 | 
			
		||||
                            bartext = bartext + "."
 | 
			
		||||
                            bar.text = bartext
 | 
			
		||||
                            f.write(chunk)
 | 
			
		||||
                #print("")
 | 
			
		||||
                #fprint("")
 | 
			
		||||
                return output_dir + "/datasheet.pdf"
 | 
			
		||||
            except KeyboardInterrupt:
 | 
			
		||||
                print("Quitting!")
 | 
			
		||||
                fprint("Quitting!")
 | 
			
		||||
                os.remove(output_dir + "/datasheet.pdf")
 | 
			
		||||
                sys.exit()
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +129,7 @@ def get_multi(partnums):
 | 
			
		||||
        def _download_image(url, output_dir): # Download datasheet with known URL
 | 
			
		||||
            global bartext
 | 
			
		||||
 | 
			
		||||
            #print(url)
 | 
			
		||||
            #fprint(url)
 | 
			
		||||
            try:
 | 
			
		||||
                with requests.get(url, stream=True) as r:
 | 
			
		||||
                    #r.raise_for_status()
 | 
			
		||||
@@ -143,27 +144,27 @@ def get_multi(partnums):
 | 
			
		||||
                            bartext = bartext + "."
 | 
			
		||||
                            bar.text = bartext
 | 
			
		||||
                            f.write(chunk)
 | 
			
		||||
                #print("")
 | 
			
		||||
                #fprint("")
 | 
			
		||||
                return output_dir + "/part-hires." + url.split(".")[-1]
 | 
			
		||||
            except KeyboardInterrupt:
 | 
			
		||||
                print("Quitting!")
 | 
			
		||||
                fprint("Quitting!")
 | 
			
		||||
                os.remove(partnum + "/datasheet.pdf")
 | 
			
		||||
                sys.exit()
 | 
			
		||||
 | 
			
		||||
        def __use_cached_datasheet(partnum, path, output_dir):
 | 
			
		||||
            print("Using cached datasheet for " + partnum, end='')
 | 
			
		||||
            fprint("Using cached datasheet for " + partnum)
 | 
			
		||||
            bar.text = "Using cached datasheet for " + partnum
 | 
			
		||||
            bar(skipped=True)
 | 
			
		||||
            print("Parsing Datasheet contents of " + partnum, end='')
 | 
			
		||||
            fprint("Parsing Datasheet contents of " + partnum)
 | 
			
		||||
            bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..."
 | 
			
		||||
            read_datasheet.parse(path, output_dir)
 | 
			
		||||
            bar(skipped=False)
 | 
			
		||||
 | 
			
		||||
        def __downloaded_datasheet(partnum, path, output_dir):
 | 
			
		||||
            print("Downloaded " + path, end='')
 | 
			
		||||
            fprint("Downloaded " + path)
 | 
			
		||||
            bar.text = "Downloaded " + path
 | 
			
		||||
            bar(skipped=False)
 | 
			
		||||
            print("Parsing Datasheet contents of " + partnum, end='')
 | 
			
		||||
            fprint("Parsing Datasheet contents of " + partnum)
 | 
			
		||||
            bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..."
 | 
			
		||||
            read_datasheet.parse(path, output_dir)
 | 
			
		||||
            bar(skipped=False)
 | 
			
		||||
@@ -182,10 +183,10 @@ def get_multi(partnums):
 | 
			
		||||
                    # Download high resolution part image if available and needed
 | 
			
		||||
                    if not os.path.exists(output_dir + "/found_part_hires"):
 | 
			
		||||
                        if _download_image(search_result["image"], output_dir):
 | 
			
		||||
                            print("Downloaded hi-res part image for " + partnum)
 | 
			
		||||
                            fprint("Downloaded hi-res part image for " + partnum)
 | 
			
		||||
                        touch(output_dir + "/found_part_hires")
 | 
			
		||||
                    else:
 | 
			
		||||
                        print("Using cached hi-res part image for " + partnum)
 | 
			
		||||
                        fprint("Using cached hi-res part image for " + partnum)
 | 
			
		||||
 | 
			
		||||
                    # Download datasheet from provided URL if needed
 | 
			
		||||
                    if os.path.exists(path) and os.path.getsize(path) > 1:
 | 
			
		||||
@@ -203,7 +204,7 @@ def get_multi(partnums):
 | 
			
		||||
 | 
			
		||||
                # Failed to download with search or guess :(
 | 
			
		||||
                else: 
 | 
			
		||||
                    print("Failed to download datasheet for part " + partnum, end='')
 | 
			
		||||
                    fprint("Failed to download datasheet for part " + partnum)
 | 
			
		||||
                    bar.text = "Failed to download datasheet for part " + partnum
 | 
			
		||||
                    failed.append(partnum)
 | 
			
		||||
                    bar(skipped=True)
 | 
			
		||||
@@ -211,13 +212,13 @@ def get_multi(partnums):
 | 
			
		||||
 | 
			
		||||
            # We already have a hi-res image and the datasheet - perfect!
 | 
			
		||||
            else:
 | 
			
		||||
                print("Using cached hi-res part image for " + partnum)
 | 
			
		||||
                fprint("Using cached hi-res part image for " + partnum)
 | 
			
		||||
                __use_cached_datasheet(partnum, path, output_dir)
 | 
			
		||||
    
 | 
			
		||||
    if len(failed) > 0:
 | 
			
		||||
        print("Failed to download:")
 | 
			
		||||
        fprint("Failed to download:")
 | 
			
		||||
        for partnum in failed:
 | 
			
		||||
            print(partnum)
 | 
			
		||||
            fprint(partnum)
 | 
			
		||||
        return False # Go to manual review upload page
 | 
			
		||||
    else:
 | 
			
		||||
        return True # All cables downloaded; we are good to go
 | 
			
		||||
@@ -238,7 +239,8 @@ if __name__ == "__main__":
 | 
			
		||||
"FDSD012A9",
 | 
			
		||||
"FSSL024NG",
 | 
			
		||||
"FISX006W0",
 | 
			
		||||
"FISX00103"
 | 
			
		||||
"FISX00103",
 | 
			
		||||
"C6D1100007"
 | 
			
		||||
    ]
 | 
			
		||||
    get_multi(partnums)
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
<html>  <head>    <title>RGB Controller Configuration</title>    <style>      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }    </style>  </head>  <body>    <h1>RGB Controller Configuration</h1><br>    <h2>Set IP address</h2>    Needs reboot to apply<br>    Set to 0.0.0.0 for DHCP    <form method="post" enctype="application/x-www-form-urlencoded" action="/postform/">      <input type="text" name="ipa" value="0" size="3">.      <input type="text" name="ipb" value="0" size="3">.      <input type="text" name="ipc" value="0" size="3">.      <input type="text" name="ipd" value="0" size="3">      <input type="submit" value="Set">    </form><br>    <h2>Set Hostname</h2>    Needs reboot to apply<br>    Max 64 characters    <form method="post" enctype="application/x-www-form-urlencoded" action="/postform/">      <input type="text" name="hostname" value="RGBController" size="20">      <input type="submit" value="Set">    </form><br>    <h2>DMX512 Start Universe</h2>    Applies immediately<br>    Between (inclusive) 1-65000    <form method="post" enctype="application/x-www-form-urlencoded" action="/postform/">      <input type="text" name="universe" value="1" size="5">      <input type="submit" value="Set">    </form><br>    <form method="post" enctype="application/x-www-form-urlencoded" action="/postform/">      <input type="submit" name="reboot" value="Reboot">    </form><br>  </body></html>
 | 
			
		||||
							
								
								
									
										94
									
								
								keyboard-down.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								keyboard-down.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
# A script to toggle the Touch Keyboard of Windows 11,
 | 
			
		||||
# compatible with both Windows PowerShell and PowerShell 7.
 | 
			
		||||
# Based on code by @torvin (https://stackoverflow.com/users/332528/torvin): https://stackoverflow.com/a/40921638
 | 
			
		||||
# Based on code by @Andrea S. (https://stackoverflow.com/users/5887913/andrea-s): https://stackoverflow.com/a/55513524
 | 
			
		||||
 | 
			
		||||
# Warning: Relies on undocumented behaviour of the Windows Shell
 | 
			
		||||
# and may break with any update.
 | 
			
		||||
# Last tested on Windows 11 Home 22000.978.
 | 
			
		||||
 | 
			
		||||
Add-Type -ReferencedAssemblies $(if ($PSVersionTable.PSEdition -eq "Desktop") {"System.Drawing.dll"} else {$null}) -Language CSharp -TypeDefinition @'
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
public class TouchKeyboardController
 | 
			
		||||
{
 | 
			
		||||
    public static void ToggleTouchKeyboard()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            UIHostNoLaunch uiHostNoLaunch = new UIHostNoLaunch();
 | 
			
		||||
            ((ITipInvocation)uiHostNoLaunch).Toggle(GetDesktopWindow());
 | 
			
		||||
            Marshal.ReleaseComObject(uiHostNoLaunch);
 | 
			
		||||
        }
 | 
			
		||||
        catch (COMException exc) 
 | 
			
		||||
        {
 | 
			
		||||
            if (exc.HResult == unchecked((int)0x80040154)) // REGDB_E_CLASSNOTREG
 | 
			
		||||
            {
 | 
			
		||||
                ProcessStartInfo processStartInfo = new ProcessStartInfo("TabTip.exe")
 | 
			
		||||
                {
 | 
			
		||||
                    UseShellExecute = true
 | 
			
		||||
                };
 | 
			
		||||
                using (Process process = Process.Start(processStartInfo))
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                throw;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
 | 
			
		||||
    class UIHostNoLaunch
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    interface ITipInvocation
 | 
			
		||||
    {
 | 
			
		||||
        void Toggle(IntPtr hwnd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [DllImport("user32.dll", SetLastError = false)]
 | 
			
		||||
    static extern IntPtr GetDesktopWindow();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static bool IsInputPaneOpen()
 | 
			
		||||
    {
 | 
			
		||||
        FrameworkInputPane frameworkInputPane = new FrameworkInputPane();
 | 
			
		||||
        Rectangle rect;
 | 
			
		||||
        ((IFrameworkInputPane)frameworkInputPane).Location(out rect);
 | 
			
		||||
        Marshal.ReleaseComObject(frameworkInputPane);
 | 
			
		||||
        return !rect.IsEmpty;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("d5120aa3-46ba-44c5-822d-ca8092c1fc72")]
 | 
			
		||||
    public class FrameworkInputPane
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("5752238b-24f0-495a-82f1-2fd593056796")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    public interface IFrameworkInputPane
 | 
			
		||||
    {
 | 
			
		||||
        int Advise([MarshalAs(UnmanagedType.IUnknown)] object pWindow, [MarshalAs(UnmanagedType.IUnknown)] object pHandler, out int pdwCookie);
 | 
			
		||||
        int AdviseWithHWND(IntPtr hwnd, [MarshalAs(UnmanagedType.IUnknown)] object pHandler, out int pdwCookie);
 | 
			
		||||
        int Unadvise(int pdwCookie);
 | 
			
		||||
        int Location(out Rectangle prcInputPaneScreenLocation);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
'@
 | 
			
		||||
 | 
			
		||||
# Toggle the Touch Keyboard regardless of whether it is currently shown or not.
 | 
			
		||||
#[TouchKeyboardController]::ToggleTouchKeyboard()
 | 
			
		||||
 | 
			
		||||
# Alternatively, if you only want to show the Touch Keyboard
 | 
			
		||||
# and not hide it if it is already active:
 | 
			
		||||
if ([TouchKeyboardController]::IsInputPaneOpen()) {
 | 
			
		||||
    [TouchKeyboardController]::ToggleTouchKeyboard()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										94
									
								
								keyboard-up.ps1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								keyboard-up.ps1
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
			
		||||
# A script to toggle the Touch Keyboard of Windows 11,
 | 
			
		||||
# compatible with both Windows PowerShell and PowerShell 7.
 | 
			
		||||
# Based on code by @torvin (https://stackoverflow.com/users/332528/torvin): https://stackoverflow.com/a/40921638
 | 
			
		||||
# Based on code by @Andrea S. (https://stackoverflow.com/users/5887913/andrea-s): https://stackoverflow.com/a/55513524
 | 
			
		||||
 | 
			
		||||
# Warning: Relies on undocumented behaviour of the Windows Shell
 | 
			
		||||
# and may break with any update.
 | 
			
		||||
# Last tested on Windows 11 Home 22000.978.
 | 
			
		||||
 | 
			
		||||
Add-Type -ReferencedAssemblies $(if ($PSVersionTable.PSEdition -eq "Desktop") {"System.Drawing.dll"} else {$null}) -Language CSharp -TypeDefinition @'
 | 
			
		||||
using System;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Drawing;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
 | 
			
		||||
public class TouchKeyboardController
 | 
			
		||||
{
 | 
			
		||||
    public static void ToggleTouchKeyboard()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            UIHostNoLaunch uiHostNoLaunch = new UIHostNoLaunch();
 | 
			
		||||
            ((ITipInvocation)uiHostNoLaunch).Toggle(GetDesktopWindow());
 | 
			
		||||
            Marshal.ReleaseComObject(uiHostNoLaunch);
 | 
			
		||||
        }
 | 
			
		||||
        catch (COMException exc) 
 | 
			
		||||
        {
 | 
			
		||||
            if (exc.HResult == unchecked((int)0x80040154)) // REGDB_E_CLASSNOTREG
 | 
			
		||||
            {
 | 
			
		||||
                ProcessStartInfo processStartInfo = new ProcessStartInfo("TabTip.exe")
 | 
			
		||||
                {
 | 
			
		||||
                    UseShellExecute = true
 | 
			
		||||
                };
 | 
			
		||||
                using (Process process = Process.Start(processStartInfo))
 | 
			
		||||
                {
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                throw;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("4ce576fa-83dc-4F88-951c-9d0782b4e376")]
 | 
			
		||||
    class UIHostNoLaunch
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("37c994e7-432b-4834-a2f7-dce1f13b834b")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    interface ITipInvocation
 | 
			
		||||
    {
 | 
			
		||||
        void Toggle(IntPtr hwnd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [DllImport("user32.dll", SetLastError = false)]
 | 
			
		||||
    static extern IntPtr GetDesktopWindow();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static bool IsInputPaneOpen()
 | 
			
		||||
    {
 | 
			
		||||
        FrameworkInputPane frameworkInputPane = new FrameworkInputPane();
 | 
			
		||||
        Rectangle rect;
 | 
			
		||||
        ((IFrameworkInputPane)frameworkInputPane).Location(out rect);
 | 
			
		||||
        Marshal.ReleaseComObject(frameworkInputPane);
 | 
			
		||||
        return !rect.IsEmpty;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("d5120aa3-46ba-44c5-822d-ca8092c1fc72")]
 | 
			
		||||
    public class FrameworkInputPane
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    [ComImport, Guid("5752238b-24f0-495a-82f1-2fd593056796")]
 | 
			
		||||
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
 | 
			
		||||
    public interface IFrameworkInputPane
 | 
			
		||||
    {
 | 
			
		||||
        int Advise([MarshalAs(UnmanagedType.IUnknown)] object pWindow, [MarshalAs(UnmanagedType.IUnknown)] object pHandler, out int pdwCookie);
 | 
			
		||||
        int AdviseWithHWND(IntPtr hwnd, [MarshalAs(UnmanagedType.IUnknown)] object pHandler, out int pdwCookie);
 | 
			
		||||
        int Unadvise(int pdwCookie);
 | 
			
		||||
        int Location(out Rectangle prcInputPaneScreenLocation);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
'@
 | 
			
		||||
 | 
			
		||||
# Toggle the Touch Keyboard regardless of whether it is currently shown or not.
 | 
			
		||||
#[TouchKeyboardController]::ToggleTouchKeyboard()
 | 
			
		||||
 | 
			
		||||
# Alternatively, if you only want to show the Touch Keyboard
 | 
			
		||||
# and not hide it if it is already active:
 | 
			
		||||
if (-not [TouchKeyboardController]::IsInputPaneOpen()) {
 | 
			
		||||
    [TouchKeyboardController]::ToggleTouchKeyboard()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										343
									
								
								led_control.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										343
									
								
								led_control.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,343 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import sacn
 | 
			
		||||
import time
 | 
			
		||||
import sys
 | 
			
		||||
import yaml
 | 
			
		||||
import math
 | 
			
		||||
import random
 | 
			
		||||
from util import fprint
 | 
			
		||||
import platform    # For getting the operating system name
 | 
			
		||||
import subprocess  # For executing a shell command
 | 
			
		||||
from util import win32
 | 
			
		||||
import cv2
 | 
			
		||||
import numpy as np
 | 
			
		||||
from uptime import uptime
 | 
			
		||||
 | 
			
		||||
sender = None
 | 
			
		||||
debug = True
 | 
			
		||||
config = None
 | 
			
		||||
leds = None
 | 
			
		||||
leds_size = None
 | 
			
		||||
leds_normalized = None
 | 
			
		||||
controllers = None
 | 
			
		||||
data = None
 | 
			
		||||
start = uptime()
 | 
			
		||||
 | 
			
		||||
def ping(host):
 | 
			
		||||
    #Returns True if host (str) responds to a ping request.
 | 
			
		||||
 | 
			
		||||
    # Option for the number of packets as a function of
 | 
			
		||||
    if win32:
 | 
			
		||||
        param1 = '-n'
 | 
			
		||||
        param2 = '-w'
 | 
			
		||||
        param3 = '250'
 | 
			
		||||
    else:
 | 
			
		||||
        param1 = '-c'
 | 
			
		||||
        param2 = '-W'
 | 
			
		||||
        param3 = '0.25'
 | 
			
		||||
 | 
			
		||||
    # Building the command. Ex: "ping -c 1 google.com"
 | 
			
		||||
    command = ['ping', param1, '1', param2, param3, host]
 | 
			
		||||
 | 
			
		||||
    return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0
 | 
			
		||||
 | 
			
		||||
def map():
 | 
			
		||||
    global config
 | 
			
		||||
    global leds
 | 
			
		||||
    global leds_size
 | 
			
		||||
    global leds_normalized
 | 
			
		||||
    global controllers
 | 
			
		||||
 | 
			
		||||
    with open('config.yml', 'r') as fileread:
 | 
			
		||||
        #global config
 | 
			
		||||
        config = yaml.safe_load(fileread)
 | 
			
		||||
 | 
			
		||||
    leds = list()
 | 
			
		||||
    leds_size = list()
 | 
			
		||||
    controllers = list()
 | 
			
		||||
    #fprint(config["led"]["map"])
 | 
			
		||||
    for shape in config["led"]["map"]:
 | 
			
		||||
        if shape["type"] == "circle":
 | 
			
		||||
            #fprint(shape["pos"])
 | 
			
		||||
            anglediv = 360.0 / shape["size"]
 | 
			
		||||
            angle = 0
 | 
			
		||||
            radius = shape["diameter"] / 2
 | 
			
		||||
            lednum = shape["start"]
 | 
			
		||||
            if len(leds) < lednum + shape["size"]:
 | 
			
		||||
                for x in range(lednum + shape["size"] - len(leds)):
 | 
			
		||||
                    leds.append(None)
 | 
			
		||||
                    leds_size.append(None)
 | 
			
		||||
            while angle < 359.999:
 | 
			
		||||
                tmpangle = angle + shape["angle"]
 | 
			
		||||
                x = math.cos(tmpangle * (math.pi / 180.0)) * radius + shape["pos"][0]
 | 
			
		||||
                y = math.sin(tmpangle * (math.pi / 180.0)) * radius + shape["pos"][1]
 | 
			
		||||
                leds[lednum] = (x,y)
 | 
			
		||||
                lednum = lednum + 1
 | 
			
		||||
                angle = angle + anglediv
 | 
			
		||||
        
 | 
			
		||||
        elif shape["type"] == "strip":
 | 
			
		||||
            angle = shape["angle"]
 | 
			
		||||
            lednum = shape["start"]
 | 
			
		||||
            length = shape["length"]
 | 
			
		||||
            distdiv = length / shape["size"]
 | 
			
		||||
            dist = distdiv / 2
 | 
			
		||||
            xmov = math.cos(angle * (math.pi / 180.0)) * distdiv
 | 
			
		||||
            ymov = math.sin(angle * (math.pi / 180.0)) * distdiv
 | 
			
		||||
            pos = shape["pos"]
 | 
			
		||||
            if len(leds) < lednum + shape["size"]:
 | 
			
		||||
                for x in range(lednum + shape["size"] - len(leds)):
 | 
			
		||||
                    leds.append(None)
 | 
			
		||||
                    leds_size.append(None)
 | 
			
		||||
            
 | 
			
		||||
            while dist < length:
 | 
			
		||||
                leds[lednum] = (pos[0], pos[1])
 | 
			
		||||
                pos[0] += xmov
 | 
			
		||||
                pos[1] += ymov
 | 
			
		||||
                dist += distdiv
 | 
			
		||||
                lednum = lednum + 1
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    flag = 0
 | 
			
		||||
    for x in leds:
 | 
			
		||||
        if x is None:
 | 
			
		||||
            flag = flag + 1
 | 
			
		||||
    if flag > 0:
 | 
			
		||||
        fprint("Warning: Imperfect LED map ordering. Hiding undefined lights.")
 | 
			
		||||
        for x in range(len(leds)):
 | 
			
		||||
            if leds[x] is None:
 | 
			
		||||
                leds[x] = (0, 0)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    #leds = tmpleds.reverse()
 | 
			
		||||
    #fprint(leds)
 | 
			
		||||
 | 
			
		||||
    # controller mapping
 | 
			
		||||
    for ctrl in config["led"]["controllers"]:
 | 
			
		||||
        if len(controllers) < ctrl["universe"]+1:
 | 
			
		||||
            for x in range(ctrl["universe"]+1 - len(controllers)):
 | 
			
		||||
                controllers.append(None)
 | 
			
		||||
 | 
			
		||||
        controllers[ctrl["universe"]] = (ctrl["ledstart"],ctrl["ledend"]+1,ctrl["ip"])
 | 
			
		||||
        for x in range(ctrl["ledstart"],ctrl["ledend"]+1):
 | 
			
		||||
            leds_size[x] = len(ctrl["mode"])
 | 
			
		||||
    #fprint(controllers)
 | 
			
		||||
    
 | 
			
		||||
    if(debug):
 | 
			
		||||
        import matplotlib.pyplot as plt
 | 
			
		||||
        plt.axis('equal')
 | 
			
		||||
        for ctrl in controllers:
 | 
			
		||||
            plt.scatter(*zip(*leds[ctrl[0]:ctrl[1]]), s=2)
 | 
			
		||||
        #plt.scatter(*zip(*leds), s=3)
 | 
			
		||||
        plt.savefig("map.png", dpi=600, bbox_inches="tight")
 | 
			
		||||
 | 
			
		||||
    leds_adj = [(x-min([led[0] for led in leds]), # push to zero start
 | 
			
		||||
                    y-min([led[1] for led in leds]) )
 | 
			
		||||
                   for x, y in leds]
 | 
			
		||||
    
 | 
			
		||||
    leds_normalized = [(x / max([led[0] for led in leds_adj]), 
 | 
			
		||||
                    y / max([led[1] for led in leds_adj]))
 | 
			
		||||
                   for x, y in leds_adj]
 | 
			
		||||
    #return leds, controllers
 | 
			
		||||
 | 
			
		||||
def init():
 | 
			
		||||
    map()
 | 
			
		||||
    global sender
 | 
			
		||||
    global config
 | 
			
		||||
    global leds
 | 
			
		||||
    global leds_size
 | 
			
		||||
    global controllers
 | 
			
		||||
    global data
 | 
			
		||||
    sender = sacn.sACNsender(fps=config["led"]["fps"], universeDiscovery=False)
 | 
			
		||||
    sender.start()  # start the sending thread
 | 
			
		||||
    for x in range(len(controllers)):
 | 
			
		||||
        print("Waiting for the controller at", controllers[x][2], "to be online...", end="")
 | 
			
		||||
        count = 0
 | 
			
		||||
        while not ping(controllers[x][2]):
 | 
			
		||||
            count = count + 1
 | 
			
		||||
            if count >= config["led"]["timeout"]:
 | 
			
		||||
                fprint(" ERROR: controller still offline after " + str(count) + " seconds, continuing...")
 | 
			
		||||
                break
 | 
			
		||||
        if count < config["led"]["timeout"]:
 | 
			
		||||
            fprint(" done")
 | 
			
		||||
    for x in range(len(controllers)):
 | 
			
		||||
        print("Activating controller", x, "at", controllers[x][2], "with", controllers[x][1]-controllers[x][0], "LEDs.")
 | 
			
		||||
        sender.activate_output(x+1)  # start sending out data
 | 
			
		||||
        sender[x+1].destination = controllers[x][2]
 | 
			
		||||
    sender.manual_flush = True
 | 
			
		||||
 | 
			
		||||
    # initialize global pixel data list
 | 
			
		||||
    data = list()
 | 
			
		||||
    for x in range(len(leds)):
 | 
			
		||||
        if leds_size[x] == 3:
 | 
			
		||||
            data.append((20,20,127))
 | 
			
		||||
        elif leds_size[x] == 4:
 | 
			
		||||
            data.append((50,50,255,0))
 | 
			
		||||
        else:
 | 
			
		||||
            data.append((0,0,0))
 | 
			
		||||
    sendall(data)
 | 
			
		||||
    #time.sleep(50000)    
 | 
			
		||||
    fprint("Running start-up test sequence...")
 | 
			
		||||
    for y in range(1):
 | 
			
		||||
        for x in range(len(leds)):
 | 
			
		||||
            setpixel(5,5,5,x)
 | 
			
		||||
        sendall(data)
 | 
			
		||||
        #time.sleep(2)
 | 
			
		||||
        #alloffsmooth()
 | 
			
		||||
 | 
			
		||||
def sendall(datain):
 | 
			
		||||
    # send all LED data to all controllers
 | 
			
		||||
    # data must have all LED data in it as [(R,G,B,)] tuples in an array, 1 tuple per pixel
 | 
			
		||||
    global controllers
 | 
			
		||||
    global sender
 | 
			
		||||
    sender.manual_flush = True
 | 
			
		||||
    for x in range(len(controllers)):
 | 
			
		||||
        sender[x+1].dmx_data = list(sum(datain[controllers[x][0]:controllers[x][1]] , ())) # flatten the subsection of the data array
 | 
			
		||||
    
 | 
			
		||||
    sender.flush()
 | 
			
		||||
    time.sleep(0.002)
 | 
			
		||||
    #sender.flush() # 100% reliable with 2 flushes, often fails with 1
 | 
			
		||||
    #time.sleep(0.002)
 | 
			
		||||
    #sender.flush()
 | 
			
		||||
 | 
			
		||||
def fastsendall(datain):
 | 
			
		||||
    # send all LED data to all controllers
 | 
			
		||||
    # data must have all LED data in it as [(R,G,B,)] tuples in an array, 1 tuple per pixel
 | 
			
		||||
    global controllers
 | 
			
		||||
    global sender
 | 
			
		||||
    sender.manual_flush = False
 | 
			
		||||
    print(datain[controllers[0][0]:controllers[0][1]])
 | 
			
		||||
    for x in range(len(controllers)):
 | 
			
		||||
        sender[x+1].dmx_data = list(sum(datain[controllers[x][0]:controllers[x][1]] , ())) # flatten the subsection of the data array
 | 
			
		||||
    
 | 
			
		||||
    sender.flush()
 | 
			
		||||
 | 
			
		||||
def senduniverse(datain, lednum):
 | 
			
		||||
    # send all LED data for 1 controller/universe
 | 
			
		||||
    # data must have all LED data in it as [(R,G,B,)] tuples in an array, 1 tuple per pixel
 | 
			
		||||
    global controllers
 | 
			
		||||
    global sender
 | 
			
		||||
    for x in range(len(controllers)):
 | 
			
		||||
        if lednum >= controllers[x][0] and lednum < controllers[x][1]:
 | 
			
		||||
            sender[x+1].dmx_data = list(sum(datain[controllers[x][0]:controllers[x][1]] , ())) # flatten the subsection of the data array
 | 
			
		||||
    
 | 
			
		||||
    sender.flush()
 | 
			
		||||
    time.sleep(0.004)
 | 
			
		||||
    #sender.flush() # 100% reliable with 2 flushes, often fails with 1
 | 
			
		||||
    #time.sleep(0.002)
 | 
			
		||||
    #sender.flush()
 | 
			
		||||
 | 
			
		||||
def alloff():
 | 
			
		||||
    tmpdata = list()
 | 
			
		||||
    for x in range(len(leds)):
 | 
			
		||||
        if leds_size[x] == 3:
 | 
			
		||||
            tmpdata.append((0,0,0))
 | 
			
		||||
        elif leds_size[x] == 4:
 | 
			
		||||
            tmpdata.append((0,0,0,0))
 | 
			
		||||
        else:
 | 
			
		||||
            tmpdata.append((0,0,0))
 | 
			
		||||
    sendall(tmpdata)
 | 
			
		||||
    #sendall(tmpdata)
 | 
			
		||||
    #sendall(tmpdata) #definitely make sure it's off
 | 
			
		||||
 | 
			
		||||
def allon():
 | 
			
		||||
    global data
 | 
			
		||||
    sendall(data)
 | 
			
		||||
 | 
			
		||||
def alloffsmooth():
 | 
			
		||||
    tmpdata = data
 | 
			
		||||
    for x in range(256):
 | 
			
		||||
        for x in range(len(data)):
 | 
			
		||||
            setpixel(tmpdata[x][0]-1,tmpdata[x][1]-1,tmpdata[x][2]-1, x)
 | 
			
		||||
        sendall(tmpdata)
 | 
			
		||||
 | 
			
		||||
    alloff()
 | 
			
		||||
 | 
			
		||||
def setpixelnow(r, g, b, num):
 | 
			
		||||
    # slight optimization: send only changed universe
 | 
			
		||||
    # unfortunately no way to manual flush data packets to only 1 controller with this sACN library
 | 
			
		||||
    global data
 | 
			
		||||
    setpixel(r,g,b,num)
 | 
			
		||||
    senduniverse(data, num)
 | 
			
		||||
 | 
			
		||||
def setpixel(r, g, b, num):
 | 
			
		||||
    global data
 | 
			
		||||
    global leds_size
 | 
			
		||||
    # constrain values
 | 
			
		||||
    if r < 0:
 | 
			
		||||
        r = 0
 | 
			
		||||
    elif r > 255:
 | 
			
		||||
        r = 255
 | 
			
		||||
    if g < 0:
 | 
			
		||||
        g = 0
 | 
			
		||||
    elif g > 255:
 | 
			
		||||
        g = 255
 | 
			
		||||
    if b < 0:
 | 
			
		||||
        b = 0
 | 
			
		||||
    elif b > 255:
 | 
			
		||||
        b = 255
 | 
			
		||||
 | 
			
		||||
    if leds_size[num] == 3:
 | 
			
		||||
        data[num] = (int(r), int(g), int(b))
 | 
			
		||||
    elif leds_size[num] == 4: # cut out matching white and turn on white pixel instead
 | 
			
		||||
        data[num] = (( int(r) - int(min(r,g,b)), int(g) - int(min(r,g,b)), int(b) - int(min(r,g,b)), int(min(r,g,b))) )
 | 
			
		||||
    else:
 | 
			
		||||
        data[num] = (int(r), int(g), int(b))
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
def close():
 | 
			
		||||
    global sender
 | 
			
		||||
    time.sleep(0.5)
 | 
			
		||||
    sender.stop()
 | 
			
		||||
 | 
			
		||||
def mapimage(image, fps=60):
 | 
			
		||||
    global start
 | 
			
		||||
    while uptime() - start < 1/fps:
 | 
			
		||||
        time.sleep(0.00001)
 | 
			
		||||
    fprint(1 / (uptime() - start))
 | 
			
		||||
    start = uptime()
 | 
			
		||||
    minsize = min(image.shape[0:2])
 | 
			
		||||
    leds_normalized2 = [(x * minsize, 
 | 
			
		||||
                        y * minsize)
 | 
			
		||||
                        for x, y in leds_normalized]
 | 
			
		||||
    
 | 
			
		||||
    cv2.imshow("video", image)
 | 
			
		||||
    cv2.waitKey(1)
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    #im_rgb = image #cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # OpenCV uses BGR format by default
 | 
			
		||||
    avgx = 0
 | 
			
		||||
    avgy = 0
 | 
			
		||||
    for xx in range(len(leds_normalized2)):
 | 
			
		||||
        led = leds_normalized2[xx]
 | 
			
		||||
        x, y = int(round(led[0])), int(round(led[1]))
 | 
			
		||||
        
 | 
			
		||||
        if x < image.shape[1] and y < image.shape[0]:
 | 
			
		||||
            #avgx += x
 | 
			
		||||
            #avgy += y
 | 
			
		||||
            color = tuple(image[y, x])
 | 
			
		||||
            setpixel(color[2]/2,color[1]/2,color[0]/2,xx) # swap b & r
 | 
			
		||||
            #print(color)
 | 
			
		||||
        else:
 | 
			
		||||
            #avgx += x
 | 
			
		||||
            #avgy += y
 | 
			
		||||
            setpixel(0,0,0,xx)
 | 
			
		||||
    #avgx /= len(leds)
 | 
			
		||||
    #avgy /= len(leds)
 | 
			
		||||
    #print((avgx,avgy, max([led[0] for led in leds_adj]), max([led[1] for led in leds_adj]) , min(image.shape[0:2]) ))
 | 
			
		||||
    global data
 | 
			
		||||
    fastsendall(data)
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    init()
 | 
			
		||||
    cap = cv2.VideoCapture('output.mp4')
 | 
			
		||||
    while cap.isOpened():
 | 
			
		||||
        ret, frame = cap.read()
 | 
			
		||||
        if not ret:
 | 
			
		||||
            break
 | 
			
		||||
        mapimage(frame)
 | 
			
		||||
 | 
			
		||||
    time.sleep(1)
 | 
			
		||||
    close()
 | 
			
		||||
    #sys.exit(0)
 | 
			
		||||
							
								
								
									
										45
									
								
								process_video.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										45
									
								
								process_video.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,45 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import cv2
 | 
			
		||||
import banner_ivu_export
 | 
			
		||||
import numpy as np
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
class qr_reader():
 | 
			
		||||
    camera = None
 | 
			
		||||
    def __init__(self, ip, port):
 | 
			
		||||
        self.camera = banner_ivu_export.DriveImg(ip, port)
 | 
			
		||||
 | 
			
		||||
    def read_qr(self, tries=1):
 | 
			
		||||
        print("Trying " + str(tries) + " frames.")
 | 
			
		||||
        for x in range(tries):
 | 
			
		||||
            try:
 | 
			
		||||
                imgtype, img = self.camera.read_img()
 | 
			
		||||
                #fprint(imgtype)
 | 
			
		||||
                image_array = np.frombuffer(img, np.uint8)
 | 
			
		||||
                img = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
 | 
			
		||||
                #cv2.imshow('Image', img)
 | 
			
		||||
                #cv2.waitKey(1)
 | 
			
		||||
                detect = cv2.QRCodeDetector()
 | 
			
		||||
                value, points, straight_qrcode = detect.detectAndDecode(img)
 | 
			
		||||
                return value
 | 
			
		||||
            except:
 | 
			
		||||
                continue
 | 
			
		||||
        return False
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
class video_streamer():
 | 
			
		||||
    camera = None
 | 
			
		||||
    def __init__(self, ip, port):
 | 
			
		||||
        self.camera = banner_ivu_export.DriveImg(ip, port)
 | 
			
		||||
 | 
			
		||||
    def get_frame(self):
 | 
			
		||||
        try:
 | 
			
		||||
            return self.camera.read_img()
 | 
			
		||||
        except:
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    test = qr_reader("192.168.1.125", 32200)
 | 
			
		||||
    while True:
 | 
			
		||||
        fprint(test.read_qr(5))
 | 
			
		||||
@@ -7,13 +7,15 @@ import camelot
 | 
			
		||||
import numpy as np
 | 
			
		||||
from PIL import Image
 | 
			
		||||
import io
 | 
			
		||||
import json
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
def parse(filename, output_dir):
 | 
			
		||||
 | 
			
		||||
    # Extract table data
 | 
			
		||||
 | 
			
		||||
    tables = camelot.read_pdf(filename, pages="1-end", flavor='lattice', backend="poppler", split_text=False, line_scale=100, process_background=True, resolution=600, interations=1, layout_kwargs={'detect_vertical': False, 'char_margin': 0.5}, shift_text=['r', 't'])
 | 
			
		||||
    #print("Total tables extracted:", tables.n)
 | 
			
		||||
    #fprint("Total tables extracted:", tables.n)
 | 
			
		||||
    n = 0
 | 
			
		||||
    pagenum = 0
 | 
			
		||||
    reader = PdfReader(filename)
 | 
			
		||||
@@ -26,10 +28,10 @@ def parse(filename, output_dir):
 | 
			
		||||
        table.df.replace(np.nan, '', inplace=True)
 | 
			
		||||
        
 | 
			
		||||
        if not table.df.empty:
 | 
			
		||||
            #print("\nTable " + str(n))
 | 
			
		||||
            #fprint("\nTable " + str(n))
 | 
			
		||||
            # Extract table names
 | 
			
		||||
            table_start = table.cells[0][0].lt[1] # Read top-left cell's top-left coordinate
 | 
			
		||||
            #print(table_start)
 | 
			
		||||
            #fprint(table_start)
 | 
			
		||||
            ymin = table_start
 | 
			
		||||
            ymax = table_start + 10
 | 
			
		||||
            if pagenum != table.page - 1:
 | 
			
		||||
@@ -43,21 +45,22 @@ def parse(filename, output_dir):
 | 
			
		||||
 | 
			
		||||
            page.extract_text(visitor_text=visitor_body)
 | 
			
		||||
            text_body = "".join(parts).strip('\n')
 | 
			
		||||
            #print(text_body)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            if len(text_body) == 0:
 | 
			
		||||
                text_body = str(n)
 | 
			
		||||
            #fprint(text_body)
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            table_list[text_body] = table.df
 | 
			
		||||
            #table.to_html("table" + str(n) + ".html")
 | 
			
		||||
            
 | 
			
		||||
            #print(table.df)
 | 
			
		||||
            #fprint(table.df)
 | 
			
		||||
            #camelot.plot(table, kind='grid').savefig("test" + str(n) + ".png")
 | 
			
		||||
            n=n+1
 | 
			
		||||
    #camelot.plot(tables[0], kind='grid').savefig("test.png")
 | 
			
		||||
    
 | 
			
		||||
    tables.export(output_dir + '/techdata.json', f='json')
 | 
			
		||||
    #tables.export(output_dir + '/techdata.json', f='json')
 | 
			
		||||
 | 
			
		||||
    # print(table_list)
 | 
			
		||||
    # fprint(table_list)
 | 
			
		||||
    # Extract Basic details - part name & description, image, etc
 | 
			
		||||
 | 
			
		||||
    reader = PdfReader(filename)
 | 
			
		||||
@@ -66,7 +69,7 @@ def parse(filename, output_dir):
 | 
			
		||||
    skip = False
 | 
			
		||||
    for image_file_object in page.images:
 | 
			
		||||
        if image_file_object.name == "img0.png" and skip == False:
 | 
			
		||||
            #print(Image.open(io.BytesIO(image_file_object.data)).mode)
 | 
			
		||||
            #fprint(Image.open(io.BytesIO(image_file_object.data)).mode)
 | 
			
		||||
            if Image.open(io.BytesIO(image_file_object.data)).mode == "P":
 | 
			
		||||
                skip = True
 | 
			
		||||
                continue
 | 
			
		||||
@@ -81,7 +84,95 @@ def parse(filename, output_dir):
 | 
			
		||||
                with open(output_dir + "/brand.png", "wb") as fp:
 | 
			
		||||
                    fp.write(image_file_object.data)
 | 
			
		||||
                    count += 1
 | 
			
		||||
    return table_list
 | 
			
		||||
 | 
			
		||||
    # Table parsing and reordring
 | 
			
		||||
    tables = dict()
 | 
			
		||||
    previous_table = ""
 | 
			
		||||
    for table_name in table_list.keys():
 | 
			
		||||
        # determine shape: horizontal or vertical
 | 
			
		||||
        table = table_list[table_name]
 | 
			
		||||
        rows = table.shape[0]
 | 
			
		||||
        cols = table.shape[1]
 | 
			
		||||
        vertical = None
 | 
			
		||||
        if rows > 2 and cols == 2:
 | 
			
		||||
            vertical = True
 | 
			
		||||
        elif cols == 1:
 | 
			
		||||
            vertical = False
 | 
			
		||||
        elif rows == 1:
 | 
			
		||||
            vertical = True
 | 
			
		||||
        elif cols == 2: # and rows <= 2
 | 
			
		||||
            # inconsistent
 | 
			
		||||
            if table.iloc[0, 0].find(":") == len(table.iloc[0, 0]) - 1: # check if last character is ":" indicating a vertical table
 | 
			
		||||
                vertical = True
 | 
			
		||||
            else:
 | 
			
		||||
                vertical = False
 | 
			
		||||
 | 
			
		||||
        elif cols > 2: # and rows <= 2
 | 
			
		||||
            vertical = False
 | 
			
		||||
        elif rows > 2 and cols > 2: # big table
 | 
			
		||||
            vertical = False
 | 
			
		||||
        else: # 1 column, <= 2 rows
 | 
			
		||||
            vertical = False
 | 
			
		||||
 | 
			
		||||
        # missing name check
 | 
			
		||||
        for table_name_2 in table_list.keys(): 
 | 
			
		||||
            if table_name_2.find(table.iloc[-1, 0]) >= 0:
 | 
			
		||||
                # Name taken from table directly above - this table does not have a name
 | 
			
		||||
                table_list["Specs " + str(len(tables))] = table_list.pop(table_name_2, None) # rename table to arbitrary altername name
 | 
			
		||||
                break
 | 
			
		||||
 | 
			
		||||
        if vertical:
 | 
			
		||||
            out = dict()
 | 
			
		||||
            for row in table.itertuples(index=False, name=None):
 | 
			
		||||
                out[row[0].replace("\n", " ").replace(":", "")] = row[1]
 | 
			
		||||
 | 
			
		||||
        else: # horizontal
 | 
			
		||||
            out = dict()
 | 
			
		||||
            for col in table.columns:
 | 
			
		||||
                col_data = tuple(table[col])
 | 
			
		||||
                out[col_data[0].replace("\n", " ")] = col_data[1:]
 | 
			
		||||
        
 | 
			
		||||
        tables[table_name] = out
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        # multi-page table check
 | 
			
		||||
        if table_name.isdigit() and len(tables) > 1:
 | 
			
		||||
            fprint(table_name)
 | 
			
		||||
            fprint(previous_table)
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            
 | 
			
		||||
            main_key = previous_table
 | 
			
		||||
            cont_key = table_name
 | 
			
		||||
            fprint(tables)
 | 
			
		||||
            if vertical == False:
 | 
			
		||||
                main_keys = list(tables[main_key].keys())
 | 
			
		||||
                for i, (cont_key, cont_values) in enumerate(tables[cont_key].items()):
 | 
			
		||||
                    if i < len(main_keys):
 | 
			
		||||
                        fprint(tables[main_key][main_keys[i]])
 | 
			
		||||
                        tables[main_key][main_keys[i]] = (tables[main_key][main_keys[i]] + (cont_key,) + cont_values)
 | 
			
		||||
 | 
			
		||||
                del tables[table_name]
 | 
			
		||||
 | 
			
		||||
            else:
 | 
			
		||||
                for key in tables[cont_key].keys():
 | 
			
		||||
                    tables[main_key][key] = tables[cont_key][key]
 | 
			
		||||
                del tables[table_name]
 | 
			
		||||
 | 
			
		||||
        previous_table = table_name
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    fprint(tables)
 | 
			
		||||
    with open(output_dir + "/tables.json", 'w') as json_file:
 | 
			
		||||
        json.dump(tables, json_file)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    return tables
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,14 @@ opencv-python
 | 
			
		||||
pypdf2==2.12.1
 | 
			
		||||
alive-progress
 | 
			
		||||
requests
 | 
			
		||||
git+https://github.com/Byeongdulee/python-urx.git
 | 
			
		||||
psycopg2
 | 
			
		||||
pyyaml
 | 
			
		||||
Flask
 | 
			
		||||
selenium
 | 
			
		||||
sacn
 | 
			
		||||
uptime
 | 
			
		||||
websockets
 | 
			
		||||
 | 
			
		||||
# Development
 | 
			
		||||
matplotlib
 | 
			
		||||
matplotlib
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										408
									
								
								run.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										408
									
								
								run.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,408 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import get_specs
 | 
			
		||||
import traceback
 | 
			
		||||
#import logging
 | 
			
		||||
import yaml
 | 
			
		||||
from multiprocessing import Process, Manager, Pool, TimeoutError, active_children, log_to_stderr, Pipe, Queue
 | 
			
		||||
from multiprocessing.pool import Pool
 | 
			
		||||
import multiprocessing
 | 
			
		||||
from time import sleep
 | 
			
		||||
from util import fprint
 | 
			
		||||
from util import run_cmd
 | 
			
		||||
import sys
 | 
			
		||||
import ur5_control
 | 
			
		||||
import os
 | 
			
		||||
import signal
 | 
			
		||||
import socket
 | 
			
		||||
from flask import Flask, render_template, request
 | 
			
		||||
import requests
 | 
			
		||||
import led_control
 | 
			
		||||
import server
 | 
			
		||||
import asyncio
 | 
			
		||||
import json
 | 
			
		||||
import process_video
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
config = None
 | 
			
		||||
keeprunning = True
 | 
			
		||||
arm_ready = False
 | 
			
		||||
led_ready = False
 | 
			
		||||
camera_ready = False
 | 
			
		||||
sensor_ready = False
 | 
			
		||||
vm_ready = False
 | 
			
		||||
killme = None
 | 
			
		||||
#pool = None
 | 
			
		||||
serverproc = None
 | 
			
		||||
camera = None
 | 
			
		||||
 | 
			
		||||
to_server_queue = Queue()
 | 
			
		||||
from_server_queue = Queue()
 | 
			
		||||
 | 
			
		||||
def arm_start_callback(res):
 | 
			
		||||
    global arm_ready
 | 
			
		||||
    arm_ready = True
 | 
			
		||||
 | 
			
		||||
def led_start_callback(res):
 | 
			
		||||
    global led_ready
 | 
			
		||||
    led_ready = True
 | 
			
		||||
 | 
			
		||||
def camera_start_callback(res):
 | 
			
		||||
    global camera_ready
 | 
			
		||||
    camera_ready = True
 | 
			
		||||
    
 | 
			
		||||
def sensor_start_callback(res):
 | 
			
		||||
    global sensor_ready
 | 
			
		||||
    sensor_ready = True
 | 
			
		||||
 | 
			
		||||
def vm_start_callback(res):
 | 
			
		||||
    global vm_ready
 | 
			
		||||
    vm_ready = True
 | 
			
		||||
 | 
			
		||||
def wait_for(val, name):
 | 
			
		||||
    #global val
 | 
			
		||||
    if val is False:
 | 
			
		||||
        fprint("waiting for " + name + " to complete...")
 | 
			
		||||
        while val is False:
 | 
			
		||||
            sleep(0.1)
 | 
			
		||||
 | 
			
		||||
def start_server_socket():
 | 
			
		||||
    """app = Flask(__name__)
 | 
			
		||||
 | 
			
		||||
    @app.route('/report_ip', methods=['POST'])
 | 
			
		||||
    def report_ip():
 | 
			
		||||
        client_ip = request.json.get('ip')
 | 
			
		||||
        fprint(f"Received IP: {client_ip}")
 | 
			
		||||
        # You can store or process the IP address as needed
 | 
			
		||||
        return "IP Received", 200
 | 
			
		||||
    
 | 
			
		||||
    app.run(host='0.0.0.0', port=5000)"""
 | 
			
		||||
    global to_server_queue
 | 
			
		||||
    global from_server_queue
 | 
			
		||||
    fprint("Starting WebSocket server...")
 | 
			
		||||
    websocket_process = server.start_websocket_server(to_server_queue, from_server_queue)
 | 
			
		||||
    
 | 
			
		||||
    # Example
 | 
			
		||||
    #to_server_queue.put("Hello, WebSocket clients!")
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    while True:
 | 
			
		||||
        #print("HI")
 | 
			
		||||
        if not from_server_queue.empty():
 | 
			
		||||
            client_id, message = from_server_queue.get()
 | 
			
		||||
            fprint(f"Message from client {client_id}: {message}")
 | 
			
		||||
 | 
			
		||||
            # Message handler
 | 
			
		||||
            try:
 | 
			
		||||
                decoded = json.loads(message)
 | 
			
		||||
                if "type" not in decoded:
 | 
			
		||||
                    fprint("Missing \"type\" field.")
 | 
			
		||||
                    continue
 | 
			
		||||
                if "call" not in decoded:
 | 
			
		||||
                    fprint("Missing \"call\" field.")
 | 
			
		||||
                    continue
 | 
			
		||||
                if "data" not in decoded:
 | 
			
		||||
                    fprint("Missing \"data\" field.")
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                # if we get here, we have a "valid" data packet
 | 
			
		||||
                data = decoded["data"]
 | 
			
		||||
                call = decoded["call"]
 | 
			
		||||
                match decoded["type"]:
 | 
			
		||||
                    case "log":
 | 
			
		||||
                        fprint("log message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("webapp: " + data)
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
 | 
			
		||||
                    case "cable_map":
 | 
			
		||||
                        fprint("cable_map message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
 | 
			
		||||
                    case "cable_details":
 | 
			
		||||
                        fprint("cable_details message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
 | 
			
		||||
                    case "cable_search":
 | 
			
		||||
                        fprint("cable_search message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
 | 
			
		||||
                    case "keyboard":
 | 
			
		||||
                        fprint("keyboard message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                            if data["enabled"] == True:
 | 
			
		||||
                                # todo : send this to client
 | 
			
		||||
                                p = Process(target=run_cmd, args=("./keyboard-up.ps1",))
 | 
			
		||||
                                p.start()
 | 
			
		||||
                            elif data["enabled"] == False:
 | 
			
		||||
                                p = Process(target=run_cmd, args=("./keyboard-down.ps1",))
 | 
			
		||||
                                p.start()
 | 
			
		||||
 | 
			
		||||
                    case "machine_settings":
 | 
			
		||||
                        fprint("machine_settings message")
 | 
			
		||||
                        if call == "send":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
                        elif call == "request":
 | 
			
		||||
                            fprint("")
 | 
			
		||||
 | 
			
		||||
                    case _:
 | 
			
		||||
                        fprint("Unknown/unimplemented data type: " + decoded["type"])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            except:
 | 
			
		||||
                fprint("Non-JSON message recieved")
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        sleep(0.001)  # Sleep to prevent tight loop
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def start_client_socket():
 | 
			
		||||
    app = Flask(__name__)
 | 
			
		||||
 | 
			
		||||
    @app.route('/control_client', methods=['POST'])
 | 
			
		||||
    def message_from_server():
 | 
			
		||||
        # Handle message from server
 | 
			
		||||
        data = request.json
 | 
			
		||||
        fprint(f"Message from server: {data.get('message')}")
 | 
			
		||||
        return "Message received", 200
 | 
			
		||||
    
 | 
			
		||||
    app.run(host='0.0.0.0', port=6000)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def check_server_online(serverip, clientip):
 | 
			
		||||
    def send_ip_to_server(server_url, client_ip):
 | 
			
		||||
        try:
 | 
			
		||||
            response = requests.post(server_url, json={'ip': client_ip}, timeout=1)
 | 
			
		||||
            fprint(f"Server response: {response.text}")
 | 
			
		||||
            return True
 | 
			
		||||
        except requests.exceptions.RequestException as e:
 | 
			
		||||
            fprint(f"Error sending IP to server: {e}")
 | 
			
		||||
            return False
 | 
			
		||||
        
 | 
			
		||||
    server_url = 'http://' + serverip + ':5000/report_ip'
 | 
			
		||||
    while not send_ip_to_server(server_url, clientip):
 | 
			
		||||
        sleep(1)
 | 
			
		||||
 | 
			
		||||
    fprint("Successfully connected to server.")
 | 
			
		||||
    return True
 | 
			
		||||
    
 | 
			
		||||
        
 | 
			
		||||
def setup_server(pool):
 | 
			
		||||
    # linux server setup
 | 
			
		||||
    global config
 | 
			
		||||
    global counter
 | 
			
		||||
    global sensor_ready
 | 
			
		||||
    global camera_ready
 | 
			
		||||
    global led_ready
 | 
			
		||||
    global arm_ready
 | 
			
		||||
    global serverproc
 | 
			
		||||
    global camera
 | 
			
		||||
 | 
			
		||||
    pool.apply_async(ur5_control.init, (config["arm"]["ip"],), callback=arm_start_callback)
 | 
			
		||||
    pool.apply_async(led_control.init, callback=led_start_callback)
 | 
			
		||||
    #pool.apply_async(sensor_control.init, callback=sensor_start_callback)
 | 
			
		||||
    serverproc = Process(target=start_server_socket)
 | 
			
		||||
    serverproc.start()
 | 
			
		||||
 | 
			
		||||
    if led_ready is False:
 | 
			
		||||
        fprint("waiting for " + "LED controller initialization" + " to complete...", sendqueue=to_server_queue)
 | 
			
		||||
        while led_ready is False:
 | 
			
		||||
            sleep(0.1)
 | 
			
		||||
    fprint("LED controllers initialized.", sendqueue=to_server_queue)
 | 
			
		||||
    #to_server_queue.put("[log] LED controllers initialized.")
 | 
			
		||||
    sensor_ready = True
 | 
			
		||||
    if sensor_ready is False:
 | 
			
		||||
        fprint("waiting for " + "Sensor Initialization" + " to complete...", sendqueue=to_server_queue)
 | 
			
		||||
        while sensor_ready is False:
 | 
			
		||||
            sleep(0.1)
 | 
			
		||||
    fprint("Sensors initialized.", sendqueue=to_server_queue)
 | 
			
		||||
 | 
			
		||||
    if camera_ready is False:
 | 
			
		||||
        fprint("waiting for " + "Camera initilization" + " to complete...", sendqueue=to_server_queue)
 | 
			
		||||
        camera = process_video.qr_reader(config["cameras"]["banner"]["ip"], config["cameras"]["banner"]["port"])
 | 
			
		||||
 | 
			
		||||
    fprint("Camera initialized.", sendqueue=to_server_queue)
 | 
			
		||||
 | 
			
		||||
    arm_ready = True
 | 
			
		||||
    if arm_ready is False:
 | 
			
		||||
        fprint("waiting for " + "UR5 initilization" + " to complete...", sendqueue=to_server_queue)
 | 
			
		||||
        while arm_ready is False:
 | 
			
		||||
            sleep(0.1)
 | 
			
		||||
    fprint("Arm initialized.", sendqueue=to_server_queue)
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
        
 | 
			
		||||
    return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def mainloop_server(pool):
 | 
			
		||||
    global config
 | 
			
		||||
    global counter
 | 
			
		||||
    global killme
 | 
			
		||||
 | 
			
		||||
    if killme.value > 0:
 | 
			
		||||
        killall()
 | 
			
		||||
    counter = counter + 1
 | 
			
		||||
 | 
			
		||||
    fprint("Looking for QR code...")
 | 
			
		||||
    print(camera.read_qr(30))
 | 
			
		||||
 | 
			
		||||
def run_loading_app():
 | 
			
		||||
    
 | 
			
		||||
    app = Flask(__name__)
 | 
			
		||||
    @app.route('/')
 | 
			
		||||
    def index():
 | 
			
		||||
        return render_template('index.html')
 | 
			
		||||
    
 | 
			
		||||
    app.run(debug=True, use_reloader=False, port=7000)
 | 
			
		||||
 | 
			
		||||
def setup_client(pool):
 | 
			
		||||
    # Windows client setup
 | 
			
		||||
    fprint("Opening browser...")
 | 
			
		||||
    firefox = webdriver.Firefox()
 | 
			
		||||
    firefox.fullscreen_window()
 | 
			
		||||
    global config
 | 
			
		||||
    global vm_ready
 | 
			
		||||
    global serverproc
 | 
			
		||||
    # Open loading wepage
 | 
			
		||||
    p = Process(target=run_loading_app)
 | 
			
		||||
    p.start()
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    firefox.get('http://localhost:7000')
 | 
			
		||||
    
 | 
			
		||||
    # start Linux server VM
 | 
			
		||||
    if config["core"]["server"] == "Hyper-V":
 | 
			
		||||
        run_cmd("Start-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
 | 
			
		||||
 | 
			
		||||
    # Wait for VM to start and be reachable over the network
 | 
			
		||||
    serverproc = Process(target=start_client_socket)
 | 
			
		||||
    serverproc.start()
 | 
			
		||||
    
 | 
			
		||||
    pool.apply_async(check_server_online, (config["core"]["serverip"],config["core"]["clientip"]), callback=vm_start_callback)
 | 
			
		||||
 | 
			
		||||
    #wait_for(vm_ready, "VM Startup")
 | 
			
		||||
 | 
			
		||||
    #global vm_ready
 | 
			
		||||
    if vm_ready is False:
 | 
			
		||||
        fprint("waiting for " + "VM Startup" + " to complete...")
 | 
			
		||||
        while vm_ready is False:
 | 
			
		||||
            sleep(0.1)
 | 
			
		||||
    p.terminate()
 | 
			
		||||
    firefox.get("http://" + config["core"]["serverip"] + ":8000")
 | 
			
		||||
    return True
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
def mainloop_client(pool):
 | 
			
		||||
    sleep(0.1)
 | 
			
		||||
 | 
			
		||||
    # listen for & act on commands from VM, if needed
 | 
			
		||||
    # mainly just shut down, possibly connect to wifi or something
 | 
			
		||||
 | 
			
		||||
"""class Logger(object):
 | 
			
		||||
    def __init__(self, filename="output.log"):
 | 
			
		||||
        self.log = open(filename, "a")
 | 
			
		||||
        self.terminal = sys.stdout
 | 
			
		||||
 | 
			
		||||
    def write(self, message):
 | 
			
		||||
        self.log.write(message)
 | 
			
		||||
        #close(filename)
 | 
			
		||||
        #self.log = open(filename, "a")
 | 
			
		||||
        try:
 | 
			
		||||
            self.terminal.write(message)
 | 
			
		||||
        except:
 | 
			
		||||
            sleep(0)
 | 
			
		||||
        
 | 
			
		||||
    def flush(self):
 | 
			
		||||
        print("",end="")"""
 | 
			
		||||
 | 
			
		||||
def killall():
 | 
			
		||||
    procs = active_children()
 | 
			
		||||
    for proc in procs:
 | 
			
		||||
        proc.kill()
 | 
			
		||||
    fprint("All child processes killed")
 | 
			
		||||
    os.kill(os.getpid(), 9) # dirty kill of self
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
def killall_signal(a, b):
 | 
			
		||||
    global config
 | 
			
		||||
    if config["core"]["server"] == "Hyper-V":
 | 
			
		||||
        run_cmd("Stop-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
 | 
			
		||||
    killall()
 | 
			
		||||
 | 
			
		||||
def error(msg, *args):
 | 
			
		||||
    return multiprocessing.get_logger().error(msg, *args)
 | 
			
		||||
 | 
			
		||||
class LogExceptions(object):
 | 
			
		||||
    def __init__(self, callable):
 | 
			
		||||
        self.__callable = callable
 | 
			
		||||
 | 
			
		||||
    def __call__(self, *args, **kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
            result = self.__callable(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            # Here we add some debugging help. If multiprocessing's
 | 
			
		||||
            # debugging is on, it will arrange to log the traceback
 | 
			
		||||
            error(traceback.format_exc())
 | 
			
		||||
            # Re-raise the original exception so the Pool worker can
 | 
			
		||||
            # clean up
 | 
			
		||||
            raise
 | 
			
		||||
 | 
			
		||||
        # It was fine, give a normal answer
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
class LoggingPool(Pool):
 | 
			
		||||
    def apply_async(self, func, args=(), kwds={}, callback=None):
 | 
			
		||||
        return Pool.apply_async(self, LogExceptions(func), args, kwds, callback)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    #sys.stdout = Logger(filename="output.log")
 | 
			
		||||
    #sys.stderr = Logger(filename="output.log")
 | 
			
		||||
    #log_to_stderr(logging.DEBUG)
 | 
			
		||||
    fprint("Starting Jukebox control system...")
 | 
			
		||||
    with open('config.yml', 'r') as fileread:
 | 
			
		||||
        #global config
 | 
			
		||||
        config = yaml.safe_load(fileread)
 | 
			
		||||
 | 
			
		||||
    with Manager() as manager:
 | 
			
		||||
        fprint("Spawning threads...")
 | 
			
		||||
        pool = LoggingPool(processes=10)
 | 
			
		||||
        counter = 0
 | 
			
		||||
        killme = manager.Value('d', 0)
 | 
			
		||||
        signal.signal(signal.SIGINT, killall_signal)
 | 
			
		||||
        if config["core"]["mode"] == "winclient":
 | 
			
		||||
            fprint("Starting in client mode.")
 | 
			
		||||
            from selenium import webdriver
 | 
			
		||||
            if setup_client(pool):
 | 
			
		||||
                fprint("Entering main loop...")
 | 
			
		||||
                while(keeprunning):
 | 
			
		||||
                    mainloop_client(pool)
 | 
			
		||||
 | 
			
		||||
        elif config["core"]["mode"] == "linuxserver":
 | 
			
		||||
            fprint("Starting in server mode.")
 | 
			
		||||
            if setup_server(pool):
 | 
			
		||||
                fprint("Entering main loop...")
 | 
			
		||||
                while(keeprunning):
 | 
			
		||||
                    mainloop_server(pool)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										95
									
								
								server.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										95
									
								
								server.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
# WebSocket server for communicating with all other nodes
 | 
			
		||||
 | 
			
		||||
"""import websockets
 | 
			
		||||
import asyncio
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
class WebSocketServer:
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.start_server = websockets.serve(self.handler, "0.0.0.0", 9000)
 | 
			
		||||
        self.received_messages = asyncio.Queue()
 | 
			
		||||
        self.connected_clients = set()
 | 
			
		||||
 | 
			
		||||
    async def handler(self, websocket, path):
 | 
			
		||||
        self.connected_clients.add(websocket)
 | 
			
		||||
        fprint(self.connected_clients)
 | 
			
		||||
        try:
 | 
			
		||||
            async for message in websocket:
 | 
			
		||||
                fprint(message)
 | 
			
		||||
                await self.received_messages.put(message)
 | 
			
		||||
        finally:
 | 
			
		||||
            self.connected_clients.remove(websocket)
 | 
			
		||||
 | 
			
		||||
    async def send_message(self, message):
 | 
			
		||||
        disconnected_clients = set()
 | 
			
		||||
        for websocket in self.connected_clients:
 | 
			
		||||
            try:
 | 
			
		||||
                await websocket.send(message)
 | 
			
		||||
            except websockets.exceptions.ConnectionClosed:
 | 
			
		||||
                disconnected_clients.add(websocket)
 | 
			
		||||
        self.connected_clients.difference_update(disconnected_clients)
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        fprint("Starting WebSocket server...")
 | 
			
		||||
        asyncio.get_event_loop().run_until_complete(self.start_server)
 | 
			
		||||
        #asyncio.run(self.start_server)
 | 
			
		||||
        asyncio.get_event_loop().run_forever()
 | 
			
		||||
 | 
			
		||||
    async def get_received_message(self):
 | 
			
		||||
        if not self.received_messages.empty():
 | 
			
		||||
            return await self.received_messages.get()
 | 
			
		||||
        return None
 | 
			
		||||
 | 
			
		||||
# Function to be used by multiprocessing to run the server
 | 
			
		||||
def run_server():
 | 
			
		||||
    server = WebSocketServer()
 | 
			
		||||
    asyncio.run(server.run())"""
 | 
			
		||||
 | 
			
		||||
import asyncio
 | 
			
		||||
import websockets
 | 
			
		||||
from multiprocessing import Process, Queue
 | 
			
		||||
from util import fprint
 | 
			
		||||
import uuid
 | 
			
		||||
 | 
			
		||||
connected_clients = {}
 | 
			
		||||
 | 
			
		||||
async def handler(websocket, path, to_server_queue, from_server_queue):
 | 
			
		||||
    # Register websocket connection
 | 
			
		||||
    client_id = str(uuid.uuid4())
 | 
			
		||||
    connected_clients[client_id] = websocket
 | 
			
		||||
    try:
 | 
			
		||||
        # Handle incoming messages
 | 
			
		||||
        async for message in websocket:
 | 
			
		||||
            #print(f"Received message: {message}")
 | 
			
		||||
            print(client_id)
 | 
			
		||||
            from_server_queue.put((client_id, message))
 | 
			
		||||
    finally:
 | 
			
		||||
        # Unregister websocket connection
 | 
			
		||||
        if client_id in connected_clients:
 | 
			
		||||
            del connected_clients[client_id]
 | 
			
		||||
            print(f"Client {client_id} connection closed")
 | 
			
		||||
 | 
			
		||||
async def send_messages(to_server_queue):
 | 
			
		||||
    while True:
 | 
			
		||||
        if not to_server_queue.empty():
 | 
			
		||||
            client_id, message = to_server_queue.get()
 | 
			
		||||
            if client_id in connected_clients:  # Send message to specific client
 | 
			
		||||
                await connected_clients[client_id].send(message)
 | 
			
		||||
            elif len(connected_clients) > 0:  # Broadcast message to all clients
 | 
			
		||||
                for client in connected_clients.values():
 | 
			
		||||
                    await client.send(message)
 | 
			
		||||
        await asyncio.sleep(0.001)
 | 
			
		||||
 | 
			
		||||
def websocket_server(to_server_queue, from_server_queue):
 | 
			
		||||
    start_server = websockets.serve(lambda ws, path: handler(ws, path, to_server_queue, from_server_queue), "localhost", 9000)
 | 
			
		||||
 | 
			
		||||
    asyncio.get_event_loop().run_until_complete(start_server)
 | 
			
		||||
    asyncio.get_event_loop().create_task(send_messages(to_server_queue))
 | 
			
		||||
    asyncio.get_event_loop().run_forever()
 | 
			
		||||
 | 
			
		||||
def start_websocket_server(to_server_queue, from_server_queue):
 | 
			
		||||
    p = Process(target=websocket_server, args=(to_server_queue, from_server_queue))
 | 
			
		||||
    p.start()
 | 
			
		||||
    return p
 | 
			
		||||
							
								
								
									
										24
									
								
								static/style.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								static/style.css
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
body {
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    padding-top: 100px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.spinner {
 | 
			
		||||
    border: 16px solid #f3f3f3;
 | 
			
		||||
    border-top: 16px solid #002554;
 | 
			
		||||
    border-radius: 50%;
 | 
			
		||||
    width: 120px;
 | 
			
		||||
    height: 120px;
 | 
			
		||||
    animation: spin 2s linear infinite;
 | 
			
		||||
    margin: auto;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@keyframes spin {
 | 
			
		||||
    0% { transform: rotate(0deg); }
 | 
			
		||||
    100% { transform: rotate(360deg); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
    margin-top: 20px;
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										13
									
								
								templates/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								templates/index.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="en">
 | 
			
		||||
<head>
 | 
			
		||||
    <meta charset="UTF-8">
 | 
			
		||||
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
    <title>Loading</title>
 | 
			
		||||
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <div class="spinner"></div>
 | 
			
		||||
    <p>Waiting for backend server to start...</p>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										58
									
								
								udp_send_test.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										58
									
								
								udp_send_test.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,58 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
 | 
			
		||||
import sacn
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
ipaddr = "192.168.68.130"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sender = None
 | 
			
		||||
data = None
 | 
			
		||||
 | 
			
		||||
start = time.time()
 | 
			
		||||
 | 
			
		||||
def init():
 | 
			
		||||
    global sender
 | 
			
		||||
    global data
 | 
			
		||||
    sender = sacn.sACNsender(universeDiscovery=False)
 | 
			
		||||
    sender.start()  # start the sending thread
 | 
			
		||||
    sender.activate_output(2)  # start sending out data
 | 
			
		||||
    sender[2].destination = ipaddr
 | 
			
		||||
    sender.manual_flush = True
 | 
			
		||||
 | 
			
		||||
    # initialize global pixel data list
 | 
			
		||||
    data = list()
 | 
			
		||||
    for x in range(170):
 | 
			
		||||
        data.append((1,2,3)) # some random data
 | 
			
		||||
 | 
			
		||||
def fastsendall(datain):
 | 
			
		||||
    # send all LED data to all controllers
 | 
			
		||||
    # data must have all LED data in it as [(R,G,B,)] tuples in an array, 1 tuple per pixel
 | 
			
		||||
    global sender
 | 
			
		||||
    sender[2].dmx_data = list(sum(datain[0:170] , ())) # flatten the subsection of the data array
 | 
			
		||||
    
 | 
			
		||||
    sender.flush()
 | 
			
		||||
 | 
			
		||||
def close():
 | 
			
		||||
    global sender
 | 
			
		||||
    time.sleep(0.5)
 | 
			
		||||
    sender.stop()
 | 
			
		||||
 | 
			
		||||
def send_fps(fps=140):
 | 
			
		||||
    global start
 | 
			
		||||
    while time.time() - start < 1/fps:
 | 
			
		||||
        time.sleep(0.00001)
 | 
			
		||||
    print("FPS:", 1 / (time.time() - start))
 | 
			
		||||
    start = time.time()
 | 
			
		||||
    global data
 | 
			
		||||
    fastsendall(data)
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    init()
 | 
			
		||||
    while True:
 | 
			
		||||
        send_fps()
 | 
			
		||||
 | 
			
		||||
    time.sleep(1)
 | 
			
		||||
    close()
 | 
			
		||||
    #sys.exit(0)
 | 
			
		||||
							
								
								
									
										101
									
								
								ur5_control.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										101
									
								
								ur5_control.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,101 @@
 | 
			
		||||
import urx
 | 
			
		||||
import math3d as m3d
 | 
			
		||||
import math
 | 
			
		||||
import time
 | 
			
		||||
import os
 | 
			
		||||
import logging
 | 
			
		||||
from urx.robotiq_two_finger_gripper import Robotiq_Two_Finger_Gripper
 | 
			
		||||
import sys
 | 
			
		||||
from util import fprint
 | 
			
		||||
 | 
			
		||||
rob = None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init(ip):
 | 
			
		||||
    global rob
 | 
			
		||||
    #sys.stdout = Logger()
 | 
			
		||||
    fprint("Starting UR5 power up...")
 | 
			
		||||
    
 | 
			
		||||
    # power up robot here
 | 
			
		||||
 | 
			
		||||
    # wait for power up (this function runs async)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # trigger auto-initialize
 | 
			
		||||
 | 
			
		||||
    # wait for auto-initialize
 | 
			
		||||
 | 
			
		||||
    # init urx
 | 
			
		||||
    fprint("Connecting to arm at " + ip)
 | 
			
		||||
    trying = True
 | 
			
		||||
    while trying:
 | 
			
		||||
        try:
 | 
			
		||||
            rob = urx.Robot(ip)
 | 
			
		||||
            trying = False
 | 
			
		||||
        except:
 | 
			
		||||
            time.sleep(1)
 | 
			
		||||
    robotiqgrip = Robotiq_Two_Finger_Gripper(rob)
 | 
			
		||||
 | 
			
		||||
    # Sets robot arm endpoint offset (x,y,z,rx,ry,rz)
 | 
			
		||||
    rob.set_tcp((0, 0, 0.15, 0, 0, 0))
 | 
			
		||||
 | 
			
		||||
    # Set weight
 | 
			
		||||
    rob.set_payload(2, (0, 0, 0.1))
 | 
			
		||||
    #rob.set_payload(2, (0, 0, 0.1))
 | 
			
		||||
    time.sleep(0.2)
 | 
			
		||||
    fprint("UR5 ready.")
 | 
			
		||||
 | 
			
		||||
def set_pos_abs(x, y, z, xb, yb, zb):
 | 
			
		||||
    global rob
 | 
			
		||||
    new_orientation = m3d.Transform()
 | 
			
		||||
    new_orientation.orient.rotate_xb(xb)  # Replace rx with the desired rotation around X-axis
 | 
			
		||||
    new_orientation.orient.rotate_yb(yb)  # Replace ry with the desired rotation around Y-axis
 | 
			
		||||
    new_orientation.orient.rotate_zb(zb)  # Replace rz with the desired rotation around Z-axis
 | 
			
		||||
 | 
			
		||||
    # Get the current pose
 | 
			
		||||
    trans = rob.getl()
 | 
			
		||||
    # Apply the new orientation while keeping the current position
 | 
			
		||||
    new_trans = m3d.Transform(new_orientation.orient, m3d.Vector(trans[0:3]))
 | 
			
		||||
    new_trans.pos.x = x
 | 
			
		||||
    new_trans.pos.y = y
 | 
			
		||||
    new_trans.pos.z = z
 | 
			
		||||
    #rob.speedj(0.2, 0.5, 99999)
 | 
			
		||||
    rob.set_pose(new_trans, acc=2, vel=2, command="movej")  # apply the new pose
 | 
			
		||||
 | 
			
		||||
def set_pos_rel_rot_abs(x, y, z, xb, yb, zb):
 | 
			
		||||
    global rob
 | 
			
		||||
    new_orientation = m3d.Transform()
 | 
			
		||||
    new_orientation.orient.rotate_xb(xb)  # Replace rx with the desired rotation around X-axis
 | 
			
		||||
    new_orientation.orient.rotate_yb(yb)  # Replace ry with the desired rotation around Y-axis
 | 
			
		||||
    new_orientation.orient.rotate_zb(zb)  # Replace rz with the desired rotation around Z-axis
 | 
			
		||||
 | 
			
		||||
    # Get the current pose
 | 
			
		||||
    trans = rob.getl()
 | 
			
		||||
    
 | 
			
		||||
    # Apply the new orientation while keeping the current position
 | 
			
		||||
    new_trans = m3d.Transform(new_orientation.orient, m3d.Vector(trans[0:3]))
 | 
			
		||||
    new_trans.pos.x += x
 | 
			
		||||
    new_trans.pos.y += y
 | 
			
		||||
    new_trans.pos.z += z
 | 
			
		||||
    #rob.speedj(0.2, 0.5, 99999)
 | 
			
		||||
    rob.set_pose(new_trans, acc=0.1, vel=0.4, command="movej")  # apply the new pose
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == "__main__":
 | 
			
		||||
    
 | 
			
		||||
    #rob.movej((0, 0, 0, 0, 0, 0), 0.1, 0.2)
 | 
			
		||||
    #rob.movel((x, y, z, rx, ry, rz), a, v)
 | 
			
		||||
    init("192.168.1.145")
 | 
			
		||||
    fprint("Current tool pose is: ",  rob.getl())
 | 
			
		||||
    #set_pos_rel_rot_abs(0, 0, -0.2, math.pi, 0, -math.pi)
 | 
			
		||||
    set_pos_abs(0.3, -0.2, 0.5, math.pi, 0, -math.pi)
 | 
			
		||||
    set_pos_abs(0, 0.2, 0.6, math.pi, 0, -math.pi)
 | 
			
		||||
    set_pos_abs(-0.5, -0.2, 0.4, math.pi, 0, -math.pi)
 | 
			
		||||
    #set_pos_rel_rot_abs(0, 0, 0, math.pi, 0, -math.pi)
 | 
			
		||||
    fprint("Current tool pose is: ",  rob.getl())
 | 
			
		||||
    rob.stop()
 | 
			
		||||
    os.kill(os.getpid(), 9) # dirty kill of self
 | 
			
		||||
    sys.exit(0)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										165
									
								
								util.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										165
									
								
								util.py
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,165 @@
 | 
			
		||||
import inspect
 | 
			
		||||
import sys
 | 
			
		||||
import subprocess
 | 
			
		||||
import os
 | 
			
		||||
from sys import platform
 | 
			
		||||
import time as t
 | 
			
		||||
from time import sleep
 | 
			
		||||
import uuid
 | 
			
		||||
import csv
 | 
			
		||||
 | 
			
		||||
win32 = platform == "win32"
 | 
			
		||||
linux = platform == "linux" or platform == "linux2"
 | 
			
		||||
macos = platform == "darwin"
 | 
			
		||||
datafile = ""
 | 
			
		||||
logMsg = ""
 | 
			
		||||
logCont = ""
 | 
			
		||||
 | 
			
		||||
settings = None
 | 
			
		||||
 | 
			
		||||
if win32:
 | 
			
		||||
    sysid = hex(uuid.getnode())
 | 
			
		||||
    # Python is running as Administrator (so netstat can get filename, to block, etc), 
 | 
			
		||||
    # so we use this to see who is actually logged in
 | 
			
		||||
    # it's very hacky
 | 
			
		||||
    startupinfo = subprocess.STARTUPINFO()
 | 
			
		||||
    #if not getattr(sys, "frozen", False):
 | 
			
		||||
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # hide powershell window
 | 
			
		||||
    res = subprocess.check_output(["WMIC", "ComputerSystem", "GET", "UserName"], universal_newlines=True, startupinfo=startupinfo)
 | 
			
		||||
    _, username = res.strip().rsplit("\n", 1)
 | 
			
		||||
    userid, sysdom = username.rsplit("\\", 1)
 | 
			
		||||
 | 
			
		||||
if linux or macos:
 | 
			
		||||
    sysid = hex(uuid.getnode())
 | 
			
		||||
    #fprint(sysid)
 | 
			
		||||
    res = subprocess.check_output(["who",], universal_newlines=True)
 | 
			
		||||
    userid = res.strip().split(" ")[0]
 | 
			
		||||
    #sysdom = subprocess.check_output(["hostname",], universal_newlines=True).strip()
 | 
			
		||||
    #fprint(sysdom)
 | 
			
		||||
    #fprint("d")
 | 
			
		||||
 | 
			
		||||
def time():
 | 
			
		||||
    return int(t.time())
 | 
			
		||||
 | 
			
		||||
def kill(pid):
 | 
			
		||||
    setup_child()
 | 
			
		||||
    try:
 | 
			
		||||
        if pid > 4:
 | 
			
		||||
            fprint("Killing PID " + str(pid), settings)
 | 
			
		||||
            os.kill(int(pid), 9)
 | 
			
		||||
            fprint("Signal 9 sent to PID " + str(pid), settings)
 | 
			
		||||
    except:
 | 
			
		||||
        fprint("Unable to kill " + str(pid), settings)
 | 
			
		||||
 | 
			
		||||
def fprint(msg, settings = None, sendqueue = None):
 | 
			
		||||
    #if not getattr(sys, "frozen", False):
 | 
			
		||||
    setup_child()
 | 
			
		||||
    try:
 | 
			
		||||
        frm = inspect.stack()[1]
 | 
			
		||||
        
 | 
			
		||||
        mod = inspect.getmodule(frm[0])
 | 
			
		||||
        logMsg = '[' + mod.__name__ + ":" + frm.function + ']:' + str(msg)
 | 
			
		||||
        
 | 
			
		||||
        print(logMsg)
 | 
			
		||||
        if (sendqueue is not None):
 | 
			
		||||
            sendqueue.put(("*", "{ \"type\": \"log\", \"call\":\"send\", \"data\": \"" + logMsg + "\" }"))
 | 
			
		||||
        if (settings is not None):
 | 
			
		||||
            tmpList = settings["logMsg"]
 | 
			
		||||
            tmpList.append(logMsg)
 | 
			
		||||
            settings["logMsg"] = tmpList
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        try:
 | 
			
		||||
            print('[????:' + frm.function + ']:', str(msg))
 | 
			
		||||
            print('[util:fprint]: ' + str(e))
 | 
			
		||||
        except:
 | 
			
		||||
            print('[????]:', str(msg))
 | 
			
		||||
        
 | 
			
		||||
        
 | 
			
		||||
   # else:
 | 
			
		||||
        #print(msg)
 | 
			
		||||
 | 
			
		||||
def find_data_file(filename):
 | 
			
		||||
    if getattr(sys, "frozen", False):
 | 
			
		||||
        # The application is frozen
 | 
			
		||||
        datadir = os.path.dirname(sys.executable)
 | 
			
		||||
    else:
 | 
			
		||||
        # The application is not frozen
 | 
			
		||||
        # Change this bit to match where you store your data files:
 | 
			
		||||
        datadir = os.path.dirname(__file__)
 | 
			
		||||
    return os.path.join(datadir, filename)
 | 
			
		||||
 | 
			
		||||
def run_cmd(cmd):
 | 
			
		||||
    if win32:
 | 
			
		||||
        startupinfo = subprocess.STARTUPINFO()
 | 
			
		||||
        #print("DICKS")
 | 
			
		||||
        #if not getattr(sys, "frozen", False):
 | 
			
		||||
        #    print("test")
 | 
			
		||||
        #    
 | 
			
		||||
        #completed = subprocess.run(["powershell", "-Command", cmd], capture_output=True, startupinfo=startupinfo)
 | 
			
		||||
        #else:
 | 
			
		||||
        #    print("alt")
 | 
			
		||||
        startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW    # , "-WindowStyle", "hidden"
 | 
			
		||||
        fprint("running PS command: " + cmd, settings)
 | 
			
		||||
        completed = subprocess.run(["powershell", "-Command", cmd], capture_output=True, startupinfo=startupinfo)
 | 
			
		||||
        fprint("ran PS command successfully", settings)
 | 
			
		||||
        #completed = subprocess.run(["powershell", "-WindowStyle", "hidden", "-Command", cmd], capture_output=True, startupinfo=startupinfo)
 | 
			
		||||
        return completed
 | 
			
		||||
    if linux or macos:
 | 
			
		||||
        fprint("running sh command: " + cmd, settings)
 | 
			
		||||
        completed = subprocess.run(["sh", "-c", cmd], capture_output=True)
 | 
			
		||||
        fprint("ran sh command successfully", settings)
 | 
			
		||||
        return completed
 | 
			
		||||
 | 
			
		||||
def setup_child(sets=None):
 | 
			
		||||
    if not getattr(sys, "frozen", False):
 | 
			
		||||
        sys.stdout = Logger(filename=find_data_file("output.log"))
 | 
			
		||||
        sys.stderr = Logger(filename=find_data_file("output.log"))
 | 
			
		||||
    if sets is not None:
 | 
			
		||||
        settings = sets
 | 
			
		||||
 | 
			
		||||
class Logger(object):
 | 
			
		||||
    def __init__(self, filename="output.log"):
 | 
			
		||||
        self.log = open(filename, "a")
 | 
			
		||||
        self.terminal = sys.stdout
 | 
			
		||||
 | 
			
		||||
    def write(self, message):
 | 
			
		||||
        self.log.write(message)
 | 
			
		||||
        #close(filename)
 | 
			
		||||
        #self.log = open(filename, "a")
 | 
			
		||||
        try:
 | 
			
		||||
            self.terminal.write(message)
 | 
			
		||||
        except:
 | 
			
		||||
            sleep(0)
 | 
			
		||||
        
 | 
			
		||||
    def flush(self):
 | 
			
		||||
        print("", end="")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def write_stats(stats):
 | 
			
		||||
    fprint("Writing stats", settings)
 | 
			
		||||
    tmp = list()
 | 
			
		||||
    tmp.append(["connections blocked", "connections allowed", "data uploaded", "data recieved", "block ratio"])
 | 
			
		||||
    tmp.append(stats)
 | 
			
		||||
    
 | 
			
		||||
    with open(find_data_file("stats.csv"), "w", newline="") as f:
 | 
			
		||||
        writer = csv.writer(f)
 | 
			
		||||
        writer.writerows(tmp)
 | 
			
		||||
    fprint("Done writing stats", settings)
 | 
			
		||||
 | 
			
		||||
def read_stats():
 | 
			
		||||
    with open(find_data_file("stats.csv"), newline='') as csvfile:
 | 
			
		||||
        csvreader = csv.reader(csvfile, delimiter=',', quotechar='|')
 | 
			
		||||
        header = True
 | 
			
		||||
        fprint(csvreader, settings)
 | 
			
		||||
        data = list()
 | 
			
		||||
        for line in csvreader:
 | 
			
		||||
            fprint(line, settings)
 | 
			
		||||
            if header:
 | 
			
		||||
                header = False
 | 
			
		||||
                continue
 | 
			
		||||
            data = line
 | 
			
		||||
        for idx in range(len(data) - 1):
 | 
			
		||||
            data[idx] = int(data[idx])
 | 
			
		||||
        data[len(data) - 1] = float(data[len(data) - 1])
 | 
			
		||||
        return data
 | 
			
		||||
            
 | 
			
		||||
							
								
								
									
										42
									
								
								websocket_test.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								websocket_test.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html>
 | 
			
		||||
<head>
 | 
			
		||||
    <title>WebSocket Test</title>
 | 
			
		||||
    <script>
 | 
			
		||||
        document.addEventListener("DOMContentLoaded", function() {
 | 
			
		||||
            // Create WebSocket connection.
 | 
			
		||||
            const socket = new WebSocket('ws://localhost:9000');
 | 
			
		||||
 | 
			
		||||
            // Connection opened
 | 
			
		||||
            socket.addEventListener('open', function (event) {
 | 
			
		||||
                console.log("Connected to WebSocket server");
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Listen for messages
 | 
			
		||||
            socket.addEventListener('message', function (event) {
 | 
			
		||||
                console.log('Message from server', event.data);
 | 
			
		||||
                let messages = document.getElementById('messages');
 | 
			
		||||
                let message = document.createElement('li');
 | 
			
		||||
                message.textContent = "Received: " + event.data;
 | 
			
		||||
                messages.appendChild(message);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Send a message to the server
 | 
			
		||||
            function sendMessage() {
 | 
			
		||||
                let message = document.getElementById('messageInput').value;
 | 
			
		||||
                socket.send(message);
 | 
			
		||||
                console.log('Message sent', message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Bind send message function to button click
 | 
			
		||||
            document.getElementById('sendMessage').addEventListener('click', sendMessage);
 | 
			
		||||
        });
 | 
			
		||||
    </script>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
    <h2>WebSocket Test</h2>
 | 
			
		||||
    <input type="text" id="messageInput" placeholder="Type a message...">
 | 
			
		||||
    <button id="sendMessage">Send Message</button>
 | 
			
		||||
    <ul id="messages"></ul>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Reference in New Issue
	
	Block a user