95 Commits

Author SHA1 Message Date
86971e0db4 Restart VM to clear network leftovers 2024-08-20 09:18:04 -05:00
f04a2a4bd4 remove pick count file 2024-08-20 09:04:56 -05:00
5b7afac517 Update web 2024-08-20 09:04:56 -05:00
ad5eb5bbb3 Update runtime 2024-08-20 09:04:56 -05:00
a822ea5184 Add QR codes for Web UI 2024-08-20 10:05:57 -05:00
5a5202e6f5 Add mode query 2024-08-16 12:12:39 -05:00
3fb78f8b5a Fix simulation mode 2024-08-16 12:09:32 -05:00
48e7c7b85f Update sensor code 2024-08-08 10:51:05 -05:00
ea5daf8689 Switch to unicode encoding 2024-08-06 17:41:45 -05:00
05baceb388 Fix logic 2024-08-06 17:36:31 -05:00
43694ca97d Use alternate method 2024-08-06 17:33:22 -05:00
3f731a4bd3 Use alternate method 2024-08-06 17:30:41 -05:00
419498cae6 Include stderr 2024-08-06 17:26:12 -05:00
c89fc86812 Fix stdout flush 2024-08-06 17:16:20 -05:00
f8507a1bc9 Add more statistics, fix camera, windows client update 2024-08-06 17:01:27 -05:00
534cc23c8d Update MQTT commands, arm height 2024-07-31 18:25:10 -05:00
b677556ece Add counter, MQTT 2024-07-30 12:54:13 -05:00
621a530530 Update web with shutdown procedure 2024-07-29 14:01:20 -05:00
0ad656728d Add shutdown routine 2024-07-29 13:50:58 -05:00
4591dc6900 Add show mode 2024-07-29 13:38:14 -05:00
1ac8712e0f Update web, disable sensors in testing mode 2024-07-29 12:06:16 -05:00
af91817a98 Match LED strip color 2024-07-25 10:43:04 -05:00
24e0094d2a Add system controller camera mode 2024-07-25 10:35:16 -05:00
faf4cc1ba8 Led loop 2024-05-14 21:52:14 -05:00
0d0ec75783 update web 2024-05-14 21:50:46 -05:00
5c4d92a84c Remove temp file 2024-05-14 21:50:11 -05:00
02ab16a566 Add windows client mode updates 2024-05-14 21:51:36 -05:00
927f61e124 update loading app 2024-05-14 20:53:01 -05:00
d69cce2e38 Add camera error message 2024-05-14 20:44:33 -05:00
98fef5ad3d increase camera timeout 2024-05-14 20:42:23 -05:00
fa25b2c8b1 add CORS 2024-05-14 20:29:32 -05:00
32260595a1 Open gripper before picking up 2024-05-14 20:21:09 -05:00
aeeb4c232a slow reset 2024-05-14 20:18:26 -05:00
bbb2b3ff17 speed up 2024-05-14 20:10:47 -05:00
b811351aba remove old var 2024-05-14 20:08:45 -05:00
38ee4f9b9a update sensor system 2024-05-14 20:07:35 -05:00
b56d176c82 test camera 2024-05-14 19:07:49 -05:00
25085f542f Try old method 2024-05-14 18:52:27 -05:00
2562fea076 try this 2024-05-14 18:48:25 -05:00
0ca9f01aa9 use boolean value 2024-05-14 18:44:54 -05:00
8b37661a84 manual open in UR5 control 2024-05-14 18:43:43 -05:00
6347cde9e2 Manually close modbus socket 2024-05-14 18:41:31 -05:00
7885c0900d add camera timeout 2024-05-14 18:32:09 -05:00
c1f783bb2c fix transition to idle after camera fail 2024-05-14 18:23:41 -05:00
a2e7592ef7 adjust 2024-05-14 18:17:58 -05:00
b0d61d7d00 Adjust slot position 2024-05-14 18:14:17 -05:00
9687a6e492 Revert to base urx, reduce camera time 2024-05-14 17:52:53 -05:00
0e6ca7490a Lower frame count 2024-05-14 17:49:10 -05:00
61954b23b9 Slow to 60hz 2024-05-14 17:44:52 -05:00
3ede455d2c Update web 2024-05-14 17:37:18 -05:00
ba6c697f00 Add return from tray in web 2024-05-14 17:34:02 -05:00
24d959da71 Update label generation, websocket listen mode 2024-05-14 17:33:47 -05:00
33af359a86 Flip vertical move 2024-05-14 16:26:32 -05:00
43f8c3b615 Other way 2024-05-14 16:24:01 -05:00
a96f34baac Readjust 2024-05-14 16:22:50 -05:00
635c2708a0 Redo arm startup motion 2024-05-14 16:16:05 -05:00
62ec1e7443 Add tray return API call 2024-05-14 16:01:10 -05:00
951ae8cdda Remove unnecessary deps that are pre-installed 2024-05-14 14:59:12 -05:00
5e0f1e93d2 prerelease 2024-05-13 02:21:07 -05:00
fab8324dea Update web 2024-05-12 18:36:07 -05:00
dbaf018c7b Remove hover effects for touchscreens 2024-05-12 18:30:04 -05:00
f47270a600 Update jukebox-web 2024-05-12 18:19:03 -05:00
e4861e0590 Add datasheet URL to cable details 2024-05-12 18:14:39 -05:00
c45c59aa62 Fix cable_search 2024-05-12 17:56:47 -05:00
1e01141f41 Make cable_search match cable_map 2024-05-12 16:59:59 -05:00
2296aea463 Add useful print statements 2024-05-10 12:58:17 -05:00
341d2232d7 Don't delete old JSON files 2024-05-10 12:51:45 -05:00
25dc18669b Downgrade camelot for better parsing 2024-05-10 12:28:59 -05:00
6c8d0bf666 Update web ui 2024-05-09 18:19:24 -05:00
be51423c89 Add real-time UR5 position updates (hopefully) 2024-05-09 18:18:55 -05:00
f416e25c1c Update LED system 2024-05-08 20:28:48 -05:00
dd7bc12fe5 Update jukebox-web 2024-05-08 16:52:28 -05:00
f06540f568 Add category field, update datasheet extracted fields 2024-05-08 16:51:01 -05:00
4a78586c34 Update jukebox-web 2024-05-08 16:44:46 -05:00
21cb2beb67 Pull description from Belden API for more consistent values 2024-05-08 16:44:22 -05:00
a905858e3b Add more descriptions 2024-05-08 15:48:26 -05:00
faf33aede7 add descriptions 2024-05-08 15:18:09 -05:00
dc4cf5c222 Center images 2024-05-08 13:27:45 -05:00
ee78037f17 Remove debug statement 2024-05-08 13:07:47 -05:00
f1f2e3191b Square up images 2024-05-08 13:04:43 -05:00
1f69dbdb82 fix clean install bug 2024-05-08 12:34:07 -05:00
36249058ac Add brand and images, fileserver for images, start to add arm queue 2024-05-08 12:29:43 -05:00
eca65dc0e0 fix typo 2024-04-29 17:49:56 -05:00
fabd1fe10a Simulation for sensors 2024-04-29 17:48:42 -05:00
b4ad57d52d Separate scanning bypass 2024-04-29 17:25:02 -05:00
72ab357dbf Improve simulation mode 2024-04-29 17:19:52 -05:00
d0083ed33f Simulation mode 2024-04-29 13:30:04 -05:00
d92ceafa57 Fix non-matching positions 2024-04-29 12:42:11 -05:00
62bcb07956 Update runtime LED modes 2024-04-29 12:33:55 -05:00
c52fe167bf Merge branch 'main' of https://git.myitr.org/Jukebox/jukebox-software 2024-04-26 17:49:14 -05:00
f7b4f264c4 Fixed positions for holder 47 and 45 in config.yaml 2024-04-26 17:49:11 -05:00
fb31ab0d73 Add LED animations to runtime 2024-04-24 14:42:47 -05:00
302d275c64 Update config 2024-04-24 14:42:47 -05:00
43392fa3ca Freeze math3d version 2024-04-24 14:42:47 -05:00
660fe29236 Update camera image parser 2024-04-24 14:42:47 -05:00
31 changed files with 4078 additions and 386 deletions

3
.gitignore vendored
View File

@ -3,6 +3,7 @@ venv
__pycache__
# cable data folder(s)
cables
cables_old
cables-sample.zip
# meilisearch (mainly where I've put the data volume for the container)
meili_data
@ -20,3 +21,5 @@ map*.png
build
# Generated label images
labels
temp
pick_count.txt

View File

@ -40,13 +40,16 @@ class DriveImg():
self.onLine = False
fprint("Offline")
def close(self):
self.trans.close()
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)
#sleep(2)
try:
self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.trans.connect((self.ip,self.PORT))
@ -55,7 +58,7 @@ class DriveImg():
except:
self.onLine = False
self.trans.close()
return resposta
return "Error", None
ret = self.trans.recv(64)
try:
valida = str(ret[0:15].decode('UTF-8'))
@ -63,15 +66,15 @@ class DriveImg():
if valida.find("TC IMAGE")<0:
self.onLine = False
self.trans.close()
sleep(2)
#sleep(2)
gravaLog(ip=self.ip,tipo="Falha",msg=f'Unable to find TC IMAGE bookmark')
return "Error"
return "Error", None
except Exception as ex:
self.onLine = False
self.trans.close()
sleep(2)
#sleep(2)
gravaLog(ip=self.ip,tipo="Falha",msg=f'Error - {str(ex)}')
return "Error"
return "Error", None
if ret:
frame = int.from_bytes(ret[24:27],"little")
isJpeg = int.from_bytes(ret[32:33],"little")
@ -113,8 +116,8 @@ class DriveImg():
#print(f'erro {str(ex)}')
self.onLine = False
self.trans.close()
sleep(2)
return resposta
#sleep(2)
return "Error", None
class DriveData():
HEADERSIZE = 100
@ -139,7 +142,7 @@ class DriveData():
if not self.onLine:
#print(f'tentando Conectar...\n')
gravaLog(ip=self.ip,msg=f'tentando Conectar...',file="log_data.txt")
sleep(2)
#sleep(2)
try:
self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.trans.connect((self.ip,self.PORT))
@ -154,7 +157,7 @@ class DriveData():
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)
#sleep(2)
return resposta

26
cables.txt Normal file
View File

@ -0,0 +1,26 @@
1172C
86104CY
FIT-221-1_4
10GXW13
10GXW53
29501F
29512
3092A
3105A
3106A
6300FE
6300UE
7922A
7958A
8760
9841
FI4X012W0
FISX012W0
IOP6U
RA500P
SPE101
SPE102
TF-1LF-006-RS5
TF-SD9-006-RI5
TT-SLG-024-HTN
3050

29
client.bat Normal file
View File

@ -0,0 +1,29 @@
@echo off
:: BatchGotAdmin
:-------------------------------------
REM --> Check for permissions
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
REM --> If error flag set, we do not have admin.
if '%errorlevel%' NEQ '0' (
echo Requesting administrative privileges...
goto UACPrompt
) else ( goto gotAdmin )
:UACPrompt
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
set params = %*:"=""
echo UAC.ShellExecute "cmd.exe", "/c %~s0 %params%", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /B
:gotAdmin
pushd "%CD%"
CD /D "%~dp0"
python run.py
:--------------------------------------

View File

@ -3,6 +3,11 @@ core:
serverip: 172.26.178.114
clientip: 172.26.176.1
server: Hyper-Vd
loopspeed: 60 # fps
mqtt:
enabled: True
server: 172.31.108.4
arm:
ip: 192.168.1.145
@ -17,67 +22,83 @@ arm:
limb3: 0.1
limb_wrist: 0.0997
cables:
port: 7900
directory: ./cables/ # must include trailing slash
#cable_map:
cameras:
banner:
ip: 192.168.1.125
port: 32200
ip: 192.168.1.199
port: 80
animation_time: 60
led:
fps: 90
fps: 100
timeout: 1
controllers:
- universe: 0
- universe: 1
ip: 192.168.1.200
ledstart: 0
ledend: 143
mode: rgb
- universe: 1
- universe: 2
ip: 192.168.1.201
ledstart: 144
ledend: 287
mode: rgb
- universe: 2
- universe: 3
ip: 192.168.1.202
ledstart: 288
ledend: 431
mode: rgb
- universe: 3
- universe: 4
ip: 192.168.1.203
ledstart: 432
ledend: 575
mode: rgb
- universe: 4
- universe: 5
ip: 192.168.1.204
ledstart: 576
ledend: 719
mode: rgb
- universe: 5
- universe: 6
ip: 192.168.1.205
ledstart: 720
ledend: 863
mode: rgb
- universe: 6
- universe: 7
ip: 192.168.1.206
ledstart: 864
ledend: 1007
mode: rgb
- universe: 7
- universe: 8
ip: 192.168.1.207
ledstart: 1008
ledend: 1151
mode: rgb
- universe: 8
- universe: 9
ip: 192.168.1.208
ledstart: 1152
ledend: 1295
mode: rgb
- universe: 9
- universe: 10
ip: 192.168.1.209
ledstart: 1296
ledend: 1365
mode: rgbw
mode: solid
color: [0, 50, 100]
- universe: 11
ip: 192.168.1.210
mode: solid
color: [0, 50, 100]
- universe: 12
ip: 192.168.1.211
mode: solid
color: [0, 50, 100]
# - universe: 0
# ip: 192.168.1.209
# ledstart: 1296
# ledend: 1365
# mode: rgbw
map:
@ -87,37 +108,37 @@ led:
start: 0
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, 304.8]
- type: circle
start: 24
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, 266.7]
- type: circle
start: 48
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, 228.6]
- type: circle
start: 72
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-197.973, 190.5]
- type: circle
start: 96
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-263.965, 152.4]
- type: circle
start: 120
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-263.965, 76.2]
# controller 2
@ -125,37 +146,37 @@ led:
start: 144
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, 228.6]
- type: circle
start: 168
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, 190.5]
- type: circle
start: 192
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, 152.4]
- type: circle
start: 216
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-197.973, 114.3]
- type: circle
start: 240
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-197.973, 38.1]
- type: circle
start: 264
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-263.965, 0]
# controller 3
@ -163,37 +184,37 @@ led:
start: 288
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, 152.4]
- type: circle
start: 312
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, 114.3]
- type: circle
start: 336
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, 76.2]
- type: circle
start: 360
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, 0]
- type: circle
start: 384
size: 24
diameter: 63.5
angle: 0
pos: [-197.973, -38.1]
angle: 180
pos: [-199.0, -35.0]
- type: circle
start: 408
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-263.965, -76.2]
# controller 4
@ -201,37 +222,37 @@ led:
start: 432
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, 76.2]
- type: circle
start: 456
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, 152.4]
- type: circle
start: 480
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, 228.6]
- type: circle
start: 504
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, 266.7]
- type: circle
start: 528
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, 190.5]
- type: circle
start: 552
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, 114.3]
@ -240,37 +261,37 @@ led:
start: 576
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, 0]
- type: circle
start: 600
size: 24
diameter: 63.5
angle: 0
pos: [197.973, 38.1]
angle: 180
pos: [201.973, 34.1]
- type: circle
start: 624
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [197.973, 114.3]
- type: circle
start: 648
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [197.973, 190.5]
- type: circle
start: 672
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [263.965, 152.4]
- type: circle
start: 696
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [263.965, 76.2]
# controller 6
@ -278,37 +299,37 @@ led:
start: 720
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, -76.2]
- type: circle
start: 744
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [197.973, -38.1]
- type: circle
start: 768
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [263.965, 0]
- type: circle
start: 792
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [263.965, -76.2]
- type: circle
start: 816
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [263.965, -152.4]
- type: circle
start: 840
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [197.973, -114.3]
# controller 7
@ -316,75 +337,75 @@ led:
start: 864
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, -114.3]
- type: circle
start: 888
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, -152.4]
- type: circle
start: 912
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, -114.3]
- type: circle
start: 936
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, -76.2]
- type: circle
start: 960
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-197.973, -114.3]
- type: circle
start: 984
size: 24
diameter: 63.5
angle: 0
pos: [-131.982, -152.4]
angle: 180
pos: [-133.0, -151.0]
# controller 8
- type: circle
start: 1008
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, -228.6]
- type: circle
start: 1032
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, -190.5]
- type: circle
start: 1056
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-65.991, -266.7]
- type: circle
start: 1080
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-131.982, -228.6]
- type: circle
start: 1104
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-197.973, -190.5]
- type: circle
start: 1128
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [-263.965, -152.4]
# controller 9
@ -392,49 +413,49 @@ led:
start: 1152
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [0, -304.8]
- type: circle
start: 1176
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, -266.7]
- type: circle
start: 1200
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, -228.6]
- type: circle
start: 1224
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [197.973, -190.5]
- type: circle
start: 1248
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [131.982, -152.4]
- type: circle
start: 1272
size: 24
diameter: 63.5
angle: 0
angle: 180
pos: [65.991, -190.5]
# Strips
- type: strip
start: 1296
size: 70
length: 600
angle: 270 # down
pos: [375, 300]
# - type: strip
# start: 1296
# size: 70
# length: 600
# angle: 270 # down
# pos: [375, 300]
global_position_offset: [0,0] # default coordinate spce below as center of arm at 0,0 - adjust if necessary
animation_time: 40
position_map:
- index: 0
pos: [-152.4, 263.965]
@ -453,7 +474,7 @@ position_map:
- index: 7
pos: [-38.1, 197.973]
- index: 8
pos: [38.1, 197.973]
pos: [34.1, 201.973]
- index: 9
pos: [114.3, 197.973]
- index: 10
@ -511,7 +532,7 @@ position_map:
- index: 36
pos: [-228.6, -131.982]
- index: 37
pos: [-152.4, -131.982]
pos: [-151.0, -133.0]
- index: 38
pos: [-76.2, -131.982]
- index: 39
@ -527,7 +548,7 @@ position_map:
- index: 44
pos: [-114.3, -197.973]
- index: 45
pos: [-38.1, -197.973]
pos: [-35.0, -199.0]
- index: 46
pos: [38.1, -197.973]
- index: 47

23
fileserver.py Normal file
View File

@ -0,0 +1,23 @@
import http.server
import socketserver
import os
class CORSHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def end_headers(self):
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
self.send_header('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type')
http.server.SimpleHTTPRequestHandler.end_headers(self)
def run_server(port, directory):
"""
Run a simple HTTP server serving files from the specified directory with CORS enabled.
"""
# Change the working directory to the specified directory
os.makedirs(directory, exist_ok=True)
os.chdir(directory)
# Create the HTTP server using the CORS-enabled handler
with socketserver.TCPServer(("", port), CORSHTTPRequestHandler) as httpd:
print(f"Serving files at port {port} with CORS enabled")
httpd.serve_forever()

View File

@ -30,6 +30,7 @@ def query_search(partnum, source):
fprint("Searching for " + partnum)
if source == "Belden":
token_url = "https://www.belden.com/coveo/rest/token?t=" + str(int(time.time()))
try:
with requests.get(token_url) as r:
out = json.loads(r.content)
token = out["token"]
@ -47,7 +48,6 @@ def query_search(partnum, source):
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try:
with requests.post(search_url, headers=headers, data=search_data) as r:
a = r.text
a = json.loads(a)
@ -81,15 +81,20 @@ def query_search(partnum, source):
brand = a["results"][idx]["raw"]["catalogitembrand"]
desc = a["results"][idx]["raw"]["catalogitemlongdesc"]
shortdesc = a["results"][idx]["raw"]["catalogitemshortdesc"]
a = json.dumps(a["results"][idx], indent=2)
app = a["results"][idx]["raw"]["catalogitemapplication"]
category = a["results"][idx]["raw"]["catalogitemfilterproductcategory"]
#a = json.dumps(a["results"][idx], indent=2)
#print(a, urlname, img, uri, dsurl)
out = dict()
out["url"] = "https://www.belden.com/products/" + uri
out["datasheet"] = "https://catalog.belden.com/techdata/EN/" + dsid + "_techdata.pdf"
out["brand"] = brand
out["name"] = shortdesc
out["short_description"] = shortdesc
out["description"] = desc
out["application"] = app
out["category"] = category
out["image"] = "https://www.belden.com" + img
out["partnum"] = name
#print(out)
@ -159,8 +164,9 @@ def touch(path):
def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
def get_multi(partnums, delay, dir, webport, cache=True, bar=None):
#with alive_bar(len(partnums) * 2, dual_line=True, calibrate=30, bar="classic2", spinner="classic", disable=True, file=sys.stdout) as bar:
failed = list()
actualpartnums = list()
def _try_download_datasheet(partnum, output_dir, dstype): # Guess datasheet URL
@ -253,7 +259,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
os.remove(partnum + "/datasheet.pdf")
sys.exit()
def __use_cached_datasheet(partnum, path, output_dir, dstype):
def __use_cached_datasheet(partnum, path, output_dir, dstype, weburl, extra):
fprint("Using cached datasheet for " + partnum)
# bar.text = "Using cached datasheet for " + partnum
# bar(skipped=True)
@ -262,7 +268,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
fprint("Parsing Datasheet contents of " + partnum)
# bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..."
out = read_datasheet.parse(path, output_dir, partnum, dstype)
out = read_datasheet.parse(path, output_dir, partnum, dstype, weburl, extra)
# bar(skipped=False)
return out
else:
@ -270,13 +276,13 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
# bar.text = "Datasheet already parsed for " + partnum + ".pdf"
# bar(skipped=True)
def __downloaded_datasheet(partnum, path, output_dir, dstype):
def __downloaded_datasheet(partnum, path, output_dir, dstype, weburl, extra):
fprint("Downloaded " + path)
# bar.text = "Downloaded " + path
# bar(skipped=False)
fprint("Parsing Datasheet contents of " + partnum)
# bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..."
out = read_datasheet.parse(path, output_dir, partnum, dstype)
out = read_datasheet.parse(path, output_dir, partnum, dstype, weburl, extra)
# bar(skipped=False)
return out
@ -289,6 +295,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
partnum = partnum.replace("/","_")
output_dir = dir + partnum
path = output_dir + "/datasheet.pdf"
weburl = ":" + str(webport) + "/" + partnum + "/"
bartext = "Downloading files for part " + partnum
# bar.text = bartext
partnum = oldpartnum.replace("_","/")
@ -300,49 +307,72 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
if search_result is not False:
# Download high resolution part image if available and needed
#oldpartnum = partnum
partnum = search_result["partnum"]
returnval = [partnum, dstype, False, False]
output_dir = dir + partnum
path = output_dir + "/datasheet.pdf"
bartext = "Downloading files for part " + partnum
os.makedirs(output_dir, exist_ok=True)
with open(output_dir + "/search-result.json", 'w') as json_file:
fprint("Saving search result of " + partnum)
json.dump(search_result, json_file)
# bar.text = bartext
if not os.path.exists(output_dir + "/found_part_hires") or not cache:
if _download_image(search_result["image"], output_dir):
fprint("Downloaded hi-res part image for " + partnum)
returnval = [partnum, dstype, True, False]
if os.path.exists(output_dir + "/parsed"):
os.remove(output_dir + "/parsed")
touch(output_dir + "/found_part_hires")
else:
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 and cache:
out = __use_cached_datasheet(partnum, path, output_dir, dstype)
out = __use_cached_datasheet(partnum, path, output_dir, dstype, weburl, search_result)
returnval = [partnum, dstype, True, out]
elif _download_datasheet(search_result["datasheet"], output_dir) is not False:
out = __downloaded_datasheet(partnum, path, output_dir, dstype)
out = __downloaded_datasheet(partnum, path, output_dir, dstype, weburl, search_result)
returnval = [partnum, dstype, True, out]
elif os.path.exists(path) and os.path.getsize(path) > 1 and cache:
out = __use_cached_datasheet(partnum, path, output_dir, dstype)
search_result = {}
if os.path.exists(output_dir + "/search-result.json"):
with open(output_dir + "/search-result.json", 'r', encoding='utf-8') as file:
search_result = json.load(file)
out = __use_cached_datasheet(partnum, path, output_dir, dstype, weburl, search_result)
returnval = [partnum, dstype, True, out]
# If search fails, and we don't already have the datasheet, guess datasheet URL and skip the hires image download
elif _try_download_datasheet(partnum, output_dir, dstype) is not False:
out = __downloaded_datasheet(partnum, path, output_dir, dstype)
search_result = {}
if os.path.exists(output_dir + "/search-result.json"):
with open(output_dir + "/search-result.json", 'r', encoding='utf-8') as file:
search_result = json.load(file)
out = __downloaded_datasheet(partnum, path, output_dir, dstype, weburl, search_result)
returnval = [partnum, dstype, False, out]
# Failed to download with search or guess :(
else:
return False
time.sleep(delay)
actualpartnums.append(returnval)
return returnval
# We already have a hi-res image and the datasheet - perfect!
else:
fprint("Using cached hi-res part image for " + partnum)
out = __use_cached_datasheet(partnum, path, output_dir, dstype)
search_result = {}
if os.path.exists(output_dir + "/search-result.json"):
with open(output_dir + "/search-result.json", 'r', encoding='utf-8') as file:
search_result = json.load(file)
out = __use_cached_datasheet(partnum, path, output_dir, dstype, weburl, search_result)
returnval = [partnum, dstype, False, out]
actualpartnums.append(returnval)
return True
@ -381,7 +411,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True, bar=None):
failed.append((partnum, dstype))
# bar(skipped=True)
# bar(skipped=True)
time.sleep(delay)
if len(failed) > 0:
fprint("Failed to download:")

51
label_document.py Executable file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python3
import fitz # PyMuPDF
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
import os
def generate_pdf(path="labels"):
# Open the existing PDF
image_folder_path = path
doc = fitz.open('label_template.pdf')
page = doc[0] # Assuming you want to read from the first page
placeholders = []
for shape in page.get_drawings():
if shape['type'] == 's': # Checking for rectangle types
placeholders.append({
'x': shape['rect'].x0,
'y': shape['rect'].y0,
'width': shape['rect'].width,
'height': shape['rect'].height
})
# List all PNG images in the folder
image_files = [f for f in os.listdir(image_folder_path) if f.endswith('.png')]
image_paths = [os.path.join(image_folder_path, file) for file in image_files]
# Create a new PDF with ReportLab
c = canvas.Canvas(path + "/print.pdf", pagesize=letter)
current_placeholder = 0 # Track the current placeholder index
for image_path in image_paths:
if current_placeholder >= len(placeholders): # Check if a new page is needed
c.showPage()
current_placeholder = 0 # Reset placeholder index for new page
# Get current placeholder
placeholder = placeholders[current_placeholder]
# Place image at the placeholder position
c.drawImage(image_path, placeholder['x'], page.rect.height - placeholder['y'] - placeholder['height'], width=placeholder['width'], height=placeholder['height'])
current_placeholder += 1
# Save the final PDF
c.save()
# Close the original PDF
doc.close()
if __name__ == "__main__":
generate_pdf("labels")

View File

@ -9,6 +9,7 @@ import os
import signal
from PIL import Image
from label_image import generate_code
from label_document import generate_pdf
def input_cable():
@ -22,7 +23,7 @@ def input_cable():
print("Input part number:", inputnum)
print("Searching databases for cables...")
# Search both AW and BL sites
status, output = get_multi(["BL"+inputnum, "AW"+inputnum], delay=0.1, dir="temp/" + str(uuid.uuid4()) + "/", cache=False)
status, output = get_multi(["BL"+inputnum, "AW"+inputnum], delay=0.1, dir="temp/" + str(uuid.uuid4()) + "/", webport=":9000", cache=False)
print("")
if len(output) > 1:
for i in output:
@ -70,6 +71,8 @@ def gen_label(partnum, path="labels"):
img = generate_code(partnum)
os.makedirs(path, exist_ok=True)
img.save(path + "/" + partnum + ".png")
generate_pdf(path)
def delete_folder(path):
# Check if the path is a directory

View File

@ -269,10 +269,10 @@ def qr_image(data, width=600):
png_image_io = "belden-logo-superhires.png"
png_image_pillow = Image.open(png_image_io)
png_width, png_height = png_image_pillow.size
png_image_pillow = png_image_pillow.resize((int(width*5.2), int(width*5.2/png_width*png_height)))
png_image_pillow = png_image_pillow.resize((int(width*4), int(width*4/png_width*png_height)))
png_width, png_height = png_image_pillow.size
# paste belden logo first because it has a big border that would cover stuff up
img.paste(png_image_pillow, (int(width*5-png_width/2), int(width*4.25 - png_height/2)))
img.paste(png_image_pillow, (int(width*5-png_width/2), int(width*3.25 - png_height/2)))
# draw circle border
#draw.arc(((width - width/5, width - width/5), (width*9 + width/5, width*9 + width/5)),0,360,fill='blue', width = int(width/8))
@ -289,7 +289,7 @@ def qr_image(data, width=600):
text_width = font.getlength(partnum[2:])
txtx = (int(width * 10) - text_width) / 2
txty = (int(width * 10)) / 2
txty = (int(width * 7.5)) / 2
# draw part number text
draw.text((txtx,txty),partnum[2:], "black", font)
@ -298,10 +298,11 @@ def qr_image(data, width=600):
qrcode = segno.make('HTTPS://BLDN.APP/' + partnum,micro=False,boost_error=False,error="L",mask=3)
out = io.BytesIO()
qrx, _ = qrcode.symbol_size(1,0)
qrcode.save(out, scale=width*2/qrx, kind="PNG", border=0)
qrcode.save(out, scale=width*3/qrx, kind="PNG", border=0)
qrimg = Image.open(out)
img.paste(qrimg, box=(int(width*4),int(width*5.75)))
img.paste(qrimg, box=(int(width*3.5),int(width*4.5)))
img = img.crop((width+int(width / 1.4)-int(width/8),width+int(width / 1.4)-int(width/8),img.size[0] - (width+int(width / 1.4)-int(width/8)), img.size[1] - (width+int(width / 1.4)-int(width/8)) ))
img = img.resize((1200, 1200), Image.LANCZOS) # 1200 dpi
return img

2064
label_template.pdf Normal file

File diff suppressed because one or more lines are too long

View File

@ -33,6 +33,11 @@ class LEDSystem():
changecount = 0
animation_time = 0
start = uptime()
solid_controllers = None
showtoggle = False
clearshow = -1
showring = -1
solidanimcount = 0
def __init__(self):
self.start = uptime()
@ -67,6 +72,7 @@ class LEDSystem():
self.leds = list()
self.leds_size = list()
self.controllers = list()
self.solid_controllers = list()
self.rings = list(range(len(self.config["position_map"])))
print("Setting ring status")
self.ringstatus = list(range(len(self.config["position_map"])))
@ -95,13 +101,13 @@ class LEDSystem():
for x in range(lednum + shape["size"] - len(self.leds)):
self.leds.append(None)
self.leds_size.append(None)
while angle < 359.999:
while angle > -359.999:
tmpangle = angle + shape["angle"]
x = math.cos(tmpangle * (math.pi / 180.0)) * radius + shape["pos"][1] # flip by 90 degress when we changed layout
y = math.sin(tmpangle * (math.pi / 180.0)) * radius + shape["pos"][0]
self.leds[lednum] = (x,y)
lednum = lednum + 1
angle = angle + anglediv
angle = angle - anglediv
elif shape["type"] == "strip":
angle = shape["angle"]
@ -157,11 +163,15 @@ class LEDSystem():
# controller mapping
for ctrl in self.config["led"]["controllers"]:
if len(self.controllers) < ctrl["universe"]+1:
for x in range(ctrl["universe"]+1 - len(self.controllers)):
if ctrl["mode"] == "solid":
self.solid_controllers.append((ctrl["universe"]-1, ctrl["color"], ctrl["ip"]))
else:
if len(self.controllers) < ctrl["universe"]:
print(ctrl["universe"])
for x in range(ctrl["universe"] - len(self.controllers)):
self.controllers.append(None)
self.controllers[ctrl["universe"]] = (ctrl["ledstart"],ctrl["ledend"]+1,ctrl["ip"])
self.controllers[ctrl["universe"]-1] = (ctrl["ledstart"],ctrl["ledend"]+1,ctrl["ip"])
for x in range(ctrl["ledstart"],ctrl["ledend"]+1):
self.leds_size[x] = len(ctrl["mode"])
#fprint(controllers)
@ -199,11 +209,29 @@ class LEDSystem():
print(" done")
#if count < self.config["led"]["timeout"]:
for x in range(len(self.solid_controllers)):
print("Waiting for the controller at", self.solid_controllers[x][2], "to be online...", end="", flush=True)
count = 0
while not self.ping(self.solid_controllers[x][2]):
count = count + 1
if count >= self.config["led"]["timeout"]:
print(" ERROR: controller still offline after " + str(count) + " seconds, continuing...")
break
else:
print(" done")
time.sleep(1)
for x in range(len(self.controllers)):
print("Activating controller", x, "at", self.controllers[x][2], "with", self.controllers[x][1]-self.controllers[x][0], "LEDs.")
print("Activating controller", x+1, "at", self.controllers[x][2], "with", self.controllers[x][1]-self.controllers[x][0], "LEDs.")
self.sender.activate_output(x+1) # start sending out data
self.sender[x+1].destination = self.controllers[x][2]
for x in range(len(self.solid_controllers)):
ctrl = self.solid_controllers[x]
print("Activating controller", ctrl[0], "at", ctrl[2], "with solid color ", ctrl[1])
self.sender.activate_output(ctrl[0]+1) # start sending out data
self.sender[ctrl[0]+1].destination = ctrl[2]
self.sender.manual_flush = True
# initialize global pixel data list
@ -212,7 +240,7 @@ class LEDSystem():
for x in range(len(self.leds)):
if self.leds_size[x] == 3:
self.exactdata.append(None)
self.data.append((20,20,127))
self.data.append((0,0,127))
elif self.leds_size[x] == 4:
self.exactdata.append(None)
self.data.append((50,50,255,0))
@ -221,13 +249,14 @@ class LEDSystem():
self.data.append((0,0,0))
self.sendall(self.data)
#time.sleep(50000)
fprint("Running start-up test sequence...")
for y in range(1):
for x in range(len(self.leds)):
self.setpixel(0,60,144,x)
self.sendall(self.data)
#time.sleep(2)
self.alloffsmooth()
# fprint("Running start-up test sequence...")
# for y in range(1):
# for x in range(len(self.leds)):
# self.setpixel(0,60,144,x)
# self.sendall(self.data)
# #time.sleep(2)
# self.alloffsmooth()
self.startup_animation(show=False)
def sendall(self, datain):
# send all LED data to all controllers
@ -236,6 +265,46 @@ class LEDSystem():
for x in range(len(self.controllers)):
self.sender[x+1].dmx_data = list(sum(datain[self.controllers[x][0]:self.controllers[x][1]] , ())) # flatten the subsection of the data array
offset = len(self.controllers)
for x in range(len(self.solid_controllers)):
ctrl = self.solid_controllers[x]
data = list((0, 0, 0)) * 170
#print(len(data))
#print(len(list(ctrl[1]) * 170))
offset = 1 - (self.solidanimcount - int(self.solidanimcount))
#print("a", offset)
data[int(self.solidanimcount) * 3] = int(ctrl[1][0]*offset)
data[int(self.solidanimcount) * 3 + 1] = int(ctrl[1][1]*offset)
data[int(self.solidanimcount) * 3 + 2] = int(ctrl[1][2]*offset)
offset = 1 - offset
#print("b", offset)
if int(self.solidanimcount) <= 168:
data[int(self.solidanimcount+1) * 3] = int(ctrl[1][0]*offset)
data[int(self.solidanimcount+1) * 3 + 1] = int(ctrl[1][1]*offset)
data[int(self.solidanimcount+1) * 3 + 2] = int(ctrl[1][2]*offset)
# if offset > 0.5: # onto next light more
# # center +1
# num = int(self.solidanimcount+1)
# else:
# num = int(self.solidanimcount)
# offset = 1 - offset
# if int(num) <= 168:
# data[int(num+1) * 3] = int(ctrl[1][0]*offset/2)
# data[int(num+1) * 3 + 1] = int(ctrl[1][1]*offset/2)
# data[int(num+1) * 3 + 2] = int(ctrl[1][2]*offset/2)
# if int(num) >= 1:
# data[int(num-1) * 3] = int(ctrl[1][0]*offset/2)
# data[int(num-1) * 3 + 1] = int(ctrl[1][1]*offset/2)
# data[int(num-1) * 3 + 2] = int(ctrl[1][2]*offset/2)
self.sender[ctrl[0]+1].dmx_data = data #list(ctrl[1]) * 170
self.solidanimcount += 0.15
if int(self.solidanimcount) >= 170:
self.solidanimcount = 0
self.sender.flush()
time.sleep(0.002)
#sender.flush() # 100% reliable with 2 flushes, often fails with 1
@ -245,8 +314,8 @@ class LEDSystem():
def fastsendall(self, 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
self.sender.manual_flush = False
print(datain[self.controllers[0][0]:self.controllers[0][1]])
self.sender.manual_flush = True
#print(datain[self.controllers[0][0]:self.controllers[0][1]])
for x in range(len(self.controllers)):
self.sender[x+1].dmx_data = list(sum(datain[self.controllers[x][0]:self.controllers[x][1]] , ())) # flatten the subsection of the data array
@ -302,15 +371,29 @@ class LEDSystem():
def setmode(self, stmode, r=0,g=0,b=0):
if stmode is not None:
if self.mode == "Show":
self.clearshow = self.showring
if self.mode != stmode:
self.firstrun = True
self.mode = stmode
return self
def setrange(self, start, end, r,g,b):
val = (r,g,b)
for x in range(start,end):
self.data[x] = val
def setallringsexcept(self, r,g,b, exclude):
startidx1 = 0
endidx2 = len(self.leds)
endidx1 = self.rings[exclude][2]
startidx2 = self.rings[exclude][3]+1
self.setrange(startidx1, endidx1, r,g,b)
self.setrange(startidx2, endidx2, r,g,b)
def setring(self, r,g,b,idx):
ring = self.rings[idx]
for pixel in range(ring[2],ring[3]):
self.setpixel(r,g,b,pixel)
@ -318,18 +401,21 @@ class LEDSystem():
#senduniverse(data, ring[2])
return self
def runmodes(self, ring = -1, speed = 1):
fprint("Mode: " + str(self.mode))
def runmodes(self, ring = -1, arm_position = None):
#fprint("Mode: " + str(self.mode))
if self.clearshow > -1:
self.setring(0,50,100,self.clearshow)
self.clearshow = -1
if self.mode == "Startup":
# loading animation. cable check
if self.firstrun:
self.changecount = self.animation_time * 3
self.changecount = self.animation_time * 2
self.firstrun = False
for x in range(len(self.ringstatus)):
self.ringstatus[x] = [True, self.animation_time]
if self.changecount > 0:
fprint(self.changecount)
#fprint(self.changecount)
self.changecount = self.fadeorder(0,len(self.leds), self.changecount, 0,50,100)
else:
self.setmode("Startup2")
@ -344,7 +430,8 @@ class LEDSystem():
if self.ringstatus[x][0]:
self.setring(0, 50, 100, x)
else:
self.ringstatus[x][1] = self.fadeall(self.rings[x][2],self.rings[x][3], self.ringstatus[x][1], 100,0,0) # not ready
#self.ringstatus[x][1] = self.fadeall(self.rings[x][2],self.rings[x][3], self.ringstatus[x][1], 100,0,0) # not ready
self.ringstatus[x][1] = self.fadeall(self.rings[x][2],self.rings[x][3], self.ringstatus[x][1], 0,50,100)
elif self.mode == "StartupCheck":
if self.firstrun:
@ -372,10 +459,11 @@ class LEDSystem():
self.firstrun = False
self.changecount = self.animation_time # 100hz
if self.changecount > 0:
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,100,0)
#self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,100,0)
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,100,0)
else:
self.setring(0,100,0,ring)
self.setmode("idle")
self.setmode("Idle")
elif self.mode == "GrabC":
if self.firstrun:
self.firstrun = False
@ -384,10 +472,74 @@ class LEDSystem():
self.changecount = self.fadeall(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,50,100)
else:
self.setring(0,50,100,ring)
self.setmode("idle")
elif self.mode == "idle":
time.sleep(0)
self.setmode("Idle")
elif self.mode == "GrabAA":
print("GrabAA")
if self.firstrun:
self.firstrun = False
self.changecount = self.animation_time # 100hz
if self.changecount > 0:
self.changecount = self.fadeall(self.rings[ring][2],self.rings[ring][3], self.changecount, 100,0,0)
else:
self.setring(100,0,0,ring)
self.setmode("Idle")
elif self.mode == "GrabAB":
if self.firstrun:
self.firstrun = False
self.changecount = self.animation_time # 100hz
if self.changecount > 0:
#self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,100,0)
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,100,0)
else:
self.setring(0,100,0,ring)
self.setmode("Idle")
elif self.mode == "Show":
if self.firstrun:
self.showring = ring
self.firstrun = False
self.showtoggle = False
self.changecount = self.animation_time / 2 # 100hz
if self.changecount > 0:
#self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,100,0)
if not self.showtoggle:
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,100,0)
else:
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 100,20,100)
else:
self.changecount = self.animation_time / 2 # 100hz
self.showtoggle = not self.showtoggle
elif self.mode == "Idle":
if self.firstrun:
self.firstrun = False
if arm_position is not None:
posxy = list()
posxy.append(int(arm_position[0] * 1000)) # x convert m to mm
posxy.append(int(arm_position[1] * 1000)) # y
radius = int(arm_position[2] * 1000) / 3
base = (0,50,100)
target = (100,100,100)
deltar = target[0] - base[0]
deltag = target[0] - base[0]
#deltab = target[0] - base[0]
# reset!
self.setallringsexcept(0,50,100, ring)
exclude = self.rings[ring][2],self.rings[ring][3]
# fade outwards
for idx,led in enumerate(self.leds):
if posxy[0] - led[0] < radius and posxy[1] - led[1] < radius and idx < exclude[0] or idx > exclude[1]:
dist = math.isqrt(int(math.pow(int(posxy[0] - led[0]), 2) + math.pow(int(posxy[1] - led[1]), 2)))
#dist = max(posxy[0] - led[0], posxy[1] - led[1])
if dist < radius:
ratio = 1 - dist/radius
#if ratio < 0:
# ratio = 0
self.data[idx] = (int(base[0] + ratio * deltar), int(base[1] + ratio * deltag), 100) #base[2] + ratio * deltab)
else:
#print("No arm position!")
pass
self.sendall(self.data)
return self
@ -439,7 +591,7 @@ class LEDSystem():
dgs /= sizerem
dbs /= sizerem
sum += abs(drs) + abs(dgs) + abs(dbs)
print(drs,dgs,dbs)
#print(drs,dgs,dbs)
for x in range(idxa,idxb):
old = self.exactdata[x]
new = list(old)
@ -522,20 +674,29 @@ class LEDSystem():
self.data[num] = (int(r), int(g), int(b))
return self
def setpixelfast3(self, rgb, num):
self.data[num] = rgb
def close(self):
time.sleep(0.5)
self.sender.stop()
return self
def mapimage(self, image, fps=90):
while uptime() - self.start < 1/fps:
time.sleep(0.00001)
fprint(1 / (uptime() - self.start))
def mapimage(self, image, fps=30):
#fprint(1 / (uptime() - self.start))
self.start = uptime()
minsize = min(image.shape[0:2])
leds_normalized2 = [(x * minsize,
if image.shape[1] > image.shape[0]:
offset = (image.shape[1] - image.shape[0])/2
leds_normalized2 = [(x * minsize + offset,
y * minsize)
for x, y in self.leds_normalized]
else:
offset = (image.shape[0] - image.shape[1])/2
leds_normalized2 = [(x * minsize,
y * minsize + offset)
for x, y in self.leds_normalized]
cv2.imshow("video", image)
cv2.waitKey(1)
@ -547,11 +708,11 @@ class LEDSystem():
for xx in range(len(leds_normalized2)):
led = leds_normalized2[xx]
x, y = int(round(led[0])), int(round(led[1]))
yinv = image.shape[0]-1
if x < image.shape[1] and y < image.shape[0]:
#avgx += x
#avgy += y
color = tuple(image[y, x])
color = tuple(image[yinv-y, x])
self.setpixel(color[2]/2,color[1]/2,color[0]/2,xx) # swap b & r
#print(color)
else:
@ -562,16 +723,21 @@ class LEDSystem():
#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]) ))
self.fastsendall(self.data)
while self.start + 1.0/fps > uptime():
time.sleep(0.00001)
return self
def mainloop(self, stmode, ring = -1, fps = 100, preview = False):
def mainloop(self, stmode, ring = -1, fps = 100, preview = False, arm_position = None):
while uptime() - self.start < 1/fps:
time.sleep(0.00001)
fprint(1 / (uptime() - self.start))
#fprint("Running LED loop with ring " + str(ring) + " and set mode " + str(stmode))
#fprint(1 / (uptime() - self.start))
self.start = uptime()
if self.mode is not None:
self.setmode(stmode)
self.runmodes(ring)
#if self.
self.runmodes(ring, arm_position)
if preview:
self.drawdata()
return self
@ -613,7 +779,7 @@ class LEDSystem():
for x in range(self.animation_time):
self.mainloop(None, preview=show)
self.clear_animations()
stmode = "StartupCheck"
stmode = "Idle"
self.mainloop(stmode, preview=show)
self.clear_animations()
return self
@ -633,23 +799,26 @@ class LEDSystem():
return self
def wait_for_animation(self, ring=-1):
while self.mode != "idle":
while self.mode != "Idle":
self.mainloop(None, ring, preview=show)
return self
if __name__ == "__main__":
import matplotlib.pyplot as plt
"""cap = cv2.VideoCapture('badapple.mp4')
ledsys = LEDSystem()
ledsys.init()
while True:
cap = cv2.VideoCapture('output.mp4')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
mapimage(frame, fps=30)"""
ledsys.mapimage(frame, fps=60)
show = False
ring = 1
ledsys = LEDSystem()
ledsys.init()
ledsys.startup_animation(show)
for x in range(54):
ledsys.ringstatus[x][0] = True

1
led_demo.bat Normal file
View File

@ -0,0 +1 @@
python led_control.py

BIN
map.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

After

Width:  |  Height:  |  Size: 344 KiB

View File

@ -4,29 +4,54 @@ import cv2
import banner_ivu_export
import numpy as np
from util import fprint
import requests
class qr_reader():
camera = None
def __init__(self, ip, port):
self.camera = banner_ivu_export.DriveImg(ip, port)
self.ip = ip
self.port = port
self.url = "http://" + ip + ":" + str(port) + "/barcode"
#self.camera = banner_ivu_export.DriveImg(ip, port)
# def read_qr(self, tries=1):
# print("Trying " + str(tries) + " frames.")
# self.camera = banner_ivu_export.DriveImg(self.ip, self.port)
# for x in range(tries):
# print(str(x) + " ", end="", flush=True)
# imgtype, img = self.camera.read_img()
# if img is not None:
# #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)
# if value != "":
# self.camera.close()
# return value
# else:
# print("\nGot no image for " + str(x))
# self.camera.close()
# return False
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
response = requests.get(self.url, timeout=tries * 15)
response.raise_for_status() # Raise an error for bad status codes
print(response.text) # Or handle the response as needed
if len(response.text) < 8:
return False
return response.text
except requests.Timeout:
print(f'The request timed out after {tries * 15} seconds')
except requests.RequestException as e:
print(f'An error occurred: {e}')
return False
class video_streamer():
camera = None
@ -41,5 +66,7 @@ class video_streamer():
if __name__ == "__main__":
test = qr_reader("192.168.1.125", 32200)
import time
while True:
fprint(test.read_qr(5))
fprint(test.read_qr(300))
time.sleep(1)

View File

@ -16,6 +16,8 @@ from util import win32
import os
import glob
import sys
from PIL import Image
import segno
def touch(path):
with open(path, 'a'):
@ -37,7 +39,7 @@ def extract_table_name(table_start, searchpage, reader, dstype, fallbackname):
ymax = table_start + 10
elif dstype == "Alphawire":
ymin = table_start - 5
ymax = table_start + 10
ymax = table_start + 20
page = reader.pages[searchpage - 1]
parts = []
def visitor_body(text, cm, tm, fontDict, fontSize):
@ -52,14 +54,52 @@ def extract_table_name(table_start, searchpage, reader, dstype, fallbackname):
return text_body
#fprint(text_body)
def parse(filename, output_dir, partnum, dstype):
def find_file_noext(directory, prefix="part-hires"):
"""
Find files in the specified directory that start with the given prefix and have any extension.
:param directory: The directory to search in.
:param prefix: The prefix to search for.
:return: A list of matching file names.
"""
# Get all files and directories in the specified directory
entries = os.listdir(directory)
# Filter files that match 'filename.EXTENSION'
matching_files = [file for file in entries if os.path.isfile(os.path.join(directory, file)) and file.split('.')[0] == prefix and len(file.split('.')) == 2]
#print(directory, matching_files)
return matching_files
def rotate_and_crop_image(path, image_name, force_rotate=False, partnum=""):
# Open the image file
fprint("Generating thumbnail image for part " + partnum)
image_path = path + "/" + image_name
with Image.open(image_path) as img:
# Check if the image is wider than it is tall
if force_rotate or img.width > img.height * 1.2:
# Rotate the image by 90 degrees counter-clockwise
img = img.rotate(90, expand=True)
# Determine the size of the square (the length of the shorter side of the image)
square_size = min(img.width, img.height)
if img.height < img.width:
offset = (img.width - img.height)/2
img_cropped = img.crop((offset, 0, square_size+offset, square_size))
else:
# Crop the image to a square from the top
img_cropped = img.crop((0, 0, square_size, square_size))
# Save or display the image
img_cropped.save(path + "/" + "thumbnail-" + image_name) # Save the cropped image
def parse(filename, output_dir, partnum, dstype, weburl, extra):
tables = []
# Extract table data
try:
if dstype == "Belden":
tables = camelot.read_pdf(filename, pages="1-end", flavor='lattice', backend="ghostscript", 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'])
elif dstype == "Alphawire":
tables = camelot.read_pdf(filename, pages="1-end", flavor='lattice', backend="ghostscript", split_text=False, line_scale=50, process_background=True, resolution=600, interations=1, layout_kwargs={'detect_vertical': True, 'char_margin': 0.5}, shift_text=['l', 't'])
tables = camelot.read_pdf(filename, pages="1-end", flavor='lattice', backend="ghostscript", split_text=False, line_scale=50, process_background=True, resolution=600, interations=1, layout_kwargs={'detect_vertical': True, 'char_margin': 0.5}, shift_text=['l', 'b'])
except (OSError, RuntimeError) as e:
print(e)
if win32:
@ -95,11 +135,26 @@ def parse(filename, output_dir, partnum, dstype):
#fprint(table_start)
text_body = extract_table_name(table_start, table.page, reader, dstype, n)
#print(text_body)
table_list[text_body] = table.df
#print(table_list[text_body])
if dstype == "Alphawire":
table_list_raw[text_body] = table
def reorder_row(row):
# Filter out NaNs and compute the original non-NaN values
non_nans = row[~row.isnull()]
# Create a new row with NaNs filled at the end
new_row = pd.Series(index=row.index)
new_row[:len(non_nans)] = non_nans
return new_row
# Apply the function to each row and return a new DataFrame
#table_list[text_body] = table.df.apply(reorder_row, axis=1)
#print(table_list[text_body])
table_list_raw[text_body] = table
#print(tbl)
#table.to_html("table" + str(n) + ".html")
#fprint(table.df)
@ -134,6 +189,27 @@ def parse(filename, output_dir, partnum, dstype):
fp.write(image_file_object.data)
count += 1
if os.path.exists(output_dir + "/found_part_hires"):
rotate_and_crop_image(output_dir, find_file_noext(output_dir, prefix="part-hires")[0], force_rotate=(dstype == "Alphawire"), partnum=partnum)
img = weburl + find_file_noext(output_dir, prefix="thumbnail-part-hires")[0]
elif len(find_file_noext(output_dir, prefix="part")) > 0:
rotate_and_crop_image(output_dir, find_file_noext(output_dir, prefix="part")[0], force_rotate=(dstype == "Alphawire"), partnum=partnum)
img = weburl + find_file_noext(output_dir, prefix="thumbnail-part")[0]
else:
img = None
fprint("Making QR code for part " + partnum)
partnumqr = partnum.replace(" ", "%20")
if dstype == "Alphawire":
partnumqr = "AW" + partnumqr
if dstype == "Belden":
partnumqr = "BL" + partnumqr
qrcode = segno.make('HTTPS://BLDN.APP/' + partnumqr,micro=False,boost_error=False,error="L",mask=3)
#out = io.BytesIO()
qrx, _ = qrcode.symbol_size(1,0)
qrcode.save(output_dir + "/qrcode.png", scale=500.0/qrx, kind="PNG", border=0, light="#00000000")
qrpath = weburl + find_file_noext(output_dir, prefix="qrcode")[0]
# Table parsing and reordring
tables = dict()
torename = dict()
@ -141,6 +217,7 @@ def parse(filename, output_dir, partnum, dstype):
#print(table_list.keys())
for table_name in table_list.keys():
# determine shape: horizontal or vertical
#print(table_name)
table = table_list[table_name]
rows = table.shape[0]
cols = table.shape[1]
@ -177,11 +254,11 @@ def parse(filename, output_dir, partnum, dstype):
if dstype == "Alphawire" and table_name_2.find("\n") >= 0:
torename[table_name_2] = table_name_2[0:table_name_2.find("\n")]
if dstype == "Alphawire" and table_name_2.find(table.iloc[-1, 0]) >= 0:
# Name taken from table directly above - this table does not have a name
torename[table_name_2] = "Specs " + str(len(tables))
#table_list["Specs " + str(len(tables))] = table_list[table_name_2] # rename table to arbitrary altername name
break
# if dstype == "Alphawire" and table_name_2.find(table.iloc[-1, 0]) >= 0:
# # Name taken from table directly above - this table does not have a name
# torename[table_name_2] = "Specs " + str(len(tables))
# #table_list["Specs " + str(len(tables))] = table_list[table_name_2] # rename table to arbitrary altername name
# break
if vertical:
out = dict()
@ -199,9 +276,9 @@ def parse(filename, output_dir, partnum, dstype):
out[col_data[0].replace("\n", " ")] = col_data[1:]
tables[table_name] = out
#print(out)
# multi-page table check, Alphawire
if dstype == "Alphawire" and table_name.isdigit():
if dstype == "Alphawire" and table_name.isdigit() and previous_table != "":
# table continues from previous page or has name on previous page
thistbl = table_list_raw[table_name]
prevtbl = table_list_raw[previous_table]
@ -294,15 +371,42 @@ def parse(filename, output_dir, partnum, dstype):
tables = replace_newlines_in_dict(tables)
# summary
#print(tables)
output_table = dict()
output_table["partnum"] = partnum
id = str(uuid.uuid4())
output_table["id"] = id
#output_table["position"] = id
#output_table["brand"] = brand
output_table["fullspecs"] = {"partnum": partnum, "id": id, **tables}
output_table["searchspecs"] = {"partnum": partnum, **flatten(tables)}
if "brand" in extra:
output_table["brand"] = extra["brand"]
else:
output_table["brand"] = dstype
output_table["datasheet"] = weburl + "datasheet.pdf"
output_table["qrcode"] = qrpath
if img is not None:
output_table["image"] = img
output_table["fullspecs"] = {"partnum": partnum, "id": id, "brand": output_table["brand"], "image": img, "datasheet": weburl + "datasheet.pdf", "qrcode": qrpath, **tables}
output_table["searchspecs"] = {"partnum": partnum, "brand": output_table["brand"], "image": img, "datasheet": weburl + "datasheet.pdf", "qrcode": qrpath, **flatten(tables)}
else:
output_table["fullspecs"] = {"partnum": partnum, "id": id, "brand": output_table["brand"], "datasheet": weburl + "datasheet.pdf", "qrcode": qrpath, **tables}
output_table["searchspecs"] = {"partnum": partnum, "brand": output_table["brand"], "datasheet": weburl + "datasheet.pdf", "qrcode": qrpath, **flatten(tables)}
if "short_description" in extra:
output_table["short_description"] = extra["short_description"]
output_table["fullspecs"]["short_description"] = extra["short_description"]
output_table["searchspecs"]["short_description"] = extra["short_description"]
if "description" in extra:
output_table["description"] = extra["description"]
output_table["fullspecs"]["description"] = extra["description"]
output_table["searchspecs"]["description"] = extra["description"]
if "application" in extra:
output_table["application"] = extra["application"]
output_table["fullspecs"]["application"] = extra["application"]
output_table["searchspecs"]["application"] = extra["application"]
if "category" in extra:
output_table["category"] = extra["category"]
output_table["fullspecs"]["category"] = extra["category"]
output_table["searchspecs"]["category"] = extra["category"]
output_table["searchspecs"]["id"] = id
@ -311,16 +415,17 @@ def parse(filename, output_dir, partnum, dstype):
#print(output_table)
#run_cmd("rm \"" + output_dir + "\"/*.json") # not reliable!
pattern = os.path.join(output_dir, '*.json')
json_files = glob.glob(pattern)
for file_path in json_files:
os.remove(file_path)
# pattern = os.path.join(output_dir, '*.json')
# json_files = glob.glob(pattern)
# for file_path in json_files:
# os.remove(file_path)
#print(f"Deleted {file_path}")
with open(output_dir + "/search.json", 'w') as json_file:
json.dump(output_table["searchspecs"], json_file)
with open(output_dir + "/specs.json", 'w') as json_file:
json.dump(output_table["fullspecs"], json_file)
fprint("Datasheet values parsed and saved for " + partnum)
#print(json.dumps(output_table, indent=2))
touch(output_dir + "/parsed") # mark as parsed
return True
@ -380,4 +485,4 @@ def flatten(tables):
if __name__ == "__main__":
print(parse("cables/3050/datasheet.pdf", "cables/3050", "3050", "Alphawire"))
print(parse("cables/3050/datasheet-new.pdf", "cables/3050", "3050", "Alphawire"))

View File

@ -1,9 +1,10 @@
# Runtime
camelot-py
opencv-python
camelot-py[base]==0.9.0
#opencv-python
pypdf2==2.12.1
alive-progress
requests
math3d==4.0.0
git+https://github.com/Byeongdulee/python-urx.git
meilisearch
pyyaml
@ -16,11 +17,12 @@ numpy
scipy
ipywidgets
pandas
pyarrow
#pyarrow
ghostscript
pyzbar
segno
pyModbusTCP
paho-mqtt
# Development
matplotlib

922
run.py

File diff suppressed because it is too large Load Diff

5
run.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
pkill -9 python # kill any old processes
cd /root/jukebox-software
python run.py

View File

@ -83,7 +83,7 @@ async def send_messages(to_server_queue):
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)
start_server = websockets.serve(lambda ws, path: handler(ws, path, to_server_queue, from_server_queue), "0.0.0.0", 9000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().create_task(send_messages(to_server_queue))

76
table0.html Normal file
View File

@ -0,0 +1,76 @@
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
</tr>
</thead>
<tbody>
<tr>
<th>2</th>
<td></td>
<td></td>
<td></td>
<td>Diameters\n(In)</td>
</tr>
<tr>
<th>4</th>
<td>1) Component\n1\n1 X 1 HOOKUP</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<th>6</th>
<td>a) Conductor\n24\n(7/32) AWG Tinned Copper</td>
<td></td>
<td></td>
<td>0.024</td>
</tr>
<tr>
<th>8</th>
<td>b)\nInsulation\n0.016" Wall, Nom. PVC</td>
<td></td>
<td></td>
<td>0.056+/-\n0.002</td>
</tr>
<tr>
<th>10</th>
<td></td>
<td>(1) Print</td>
<td>ALPHA WIRE\nE163869-* RU AWM STYLES\n1569\n105C\nOR 1007\n80C 300V VW-1\nIEC 60332-2\n24 AWG\nOR CRU TR-64\n90C FT1 CE ROHS\n{0}\n* = Factory Code</td>
<td></td>
</tr>
<tr>
<th>12</th>
<td></td>
<td>(2) Color(s)</td>
<td>WHITE, BLACK, GREEN,\nYELLOW, BLUE, BROWN, ORANGE\nSLATE, VIOLET, WHITE/BLACK, WHITE/RED</td>
<td></td>
</tr>
<tr>
<th>13</th>
<td></td>
<td></td>
<td>WHITE/GREEN, WHITE/YELLOW, WHITE/BLUE\nWHITE/BROWN, WHITE/ORANGE, WHITE/SLATE\nWHITE/VIOLET, GREEN/YELLOW,\nPINK, DK. BLUE</td>
<td></td>
</tr>
<tr>
<th>14</th>
<td></td>
<td></td>
<td>BROWN/BLACK, WHITE/PINK, BROWN/GREEN, BROWN/BLUE</td>
<td></td>
</tr>
<tr>
<th>15</th>
<td></td>
<td></td>
<td>RED</td>
<td></td>
</tr>
</tbody>
</table>

46
table1.html Normal file
View File

@ -0,0 +1,46 @@
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>1</th>
<th>2</th>
</tr>
</thead>
<tbody>
<tr>
<th>2</th>
<td>1) UL</td>
<td>AWM/STYLE\n1007\n80°C /\n300 VRMS</td>
</tr>
<tr>
<th>4</th>
<td></td>
<td>AWM/STYLE\n1569\n105°C /\n300 VRMS</td>
</tr>
<tr>
<th>6</th>
<td></td>
<td>VW-1</td>
</tr>
<tr>
<th>8</th>
<td>2) CSA International</td>
<td>TR-64\n90°C</td>
</tr>
<tr>
<th>10</th>
<td></td>
<td>FT1</td>
</tr>
<tr>
<th>12</th>
<td>3)\nIEC</td>
<td>EN 60332-2\nFlame Behavior</td>
</tr>
<tr>
<th>14</th>
<td>4) CE:</td>
<td>EU Low Voltage Directive\n2014/35/EU</td>
</tr>
</tbody>
</table>

60
table2.html Normal file
View File

@ -0,0 +1,60 @@
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>1</th>
<th>2</th>
<th>4</th>
</tr>
</thead>
<tbody>
<tr>
<th>2</th>
<td>1)\nEU Directive\n2011/65/EU(RoHS2),\nEU Directive\n2015/863/EU (RoHS3):</td>
<td></td>
<td></td>
</tr>
<tr>
<th>4</th>
<td></td>
<td></td>
<td>All materials used in the manufacture of\nthis part\nare\nin compliance with European Directive\n2011/65/EU</td>
</tr>
<tr>
<th>5</th>
<td></td>
<td></td>
<td>and the\namending Directive\n2015/863/EU of\n4\nJune\n2015\nregarding the\nrestriction of use of\ncertain</td>
</tr>
<tr>
<th>6</th>
<td></td>
<td></td>
<td>hazardous\nsubstances\nin electrical\nand electronic\nequipment.</td>
</tr>
<tr>
<th>7</th>
<td>2) REACH Regulation (EC 1907/2006):</td>
<td></td>
<td></td>
</tr>
<tr>
<th>8</th>
<td></td>
<td></td>
<td>This product does not\ncontain Substances of Very High Concern (SVHC)\nlisted on the\nEuropean Union's</td>
</tr>
<tr>
<th>9</th>
<td></td>
<td></td>
<td>REACH candidate\nlist\nin excess of\n0.1% mass of\nthe\nitem.</td>
</tr>
<tr>
<th>11</th>
<td></td>
<td>3) California Proposition 65:</td>
<td>This product may\ncontain substances\nknown to the\nState of California\nto cause Cancer or Reproductive\nHarm, but\nis\nexempt\nfrom labeling based on the Consent\nJudgement.\nSee\nthe Alpha Wire website\nfor more\ninformation.</td>
</tr>
</tbody>
</table>

60
table3.html Normal file
View File

@ -0,0 +1,60 @@
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
</thead>
<tbody>
<tr>
<th>2</th>
<td></td>
<td>Physical &amp; Mechanical Properties</td>
<td></td>
</tr>
<tr>
<th>4</th>
<td>1)\nTemperature Range</td>
<td></td>
<td>-40\nto 105°C</td>
</tr>
<tr>
<th>6</th>
<td>2) Bend Radius</td>
<td></td>
<td>10X Cable Diameter</td>
</tr>
<tr>
<th>8</th>
<td>3) Pull\nTension</td>
<td></td>
<td>3.5\nLbs, Maximum</td>
</tr>
<tr>
<th>9</th>
<td></td>
<td>Electrical Properties\n(For\nEngineering purposes only)</td>
<td></td>
</tr>
<tr>
<th>11</th>
<td>1) Voltage Rating</td>
<td></td>
<td>300 VRMS</td>
</tr>
<tr>
<th>13</th>
<td>2)\nInductance</td>
<td></td>
<td>0.07 μH/ft, Nominal</td>
</tr>
<tr>
<th>15</th>
<td>3) Conductor DCR</td>
<td></td>
<td>25 Ω/1000ft @20°C, Nominal</td>
</tr>
</tbody>
</table>

18
table4.html Normal file
View File

@ -0,0 +1,18 @@
<table border="1" class="dataframe">
<thead>
<tr style="text-align: right;">
<th></th>
<th>1</th>
</tr>
</thead>
<tbody>
<tr>
<th>2</th>
<td>Packaging\nFlange\nx\nTraverse\nx Barrel\n(inches)</td>
</tr>
<tr>
<th>3</th>
<td>a)\n32800\nFT\n23\nx\n15\nx\n0 Max.\n4\nseparate pieces; Min length/piece\n10000\nFT.\nb)\n10000\nFT\n12\nx\n5.94\nx\n5 Max.\n3\nseparate pieces; Min length/piece\n1000\nFT.\nc)\n5000\nFT\n10.5\nx\n5\nx\n3.5 Continuous\nlength\nd)\n1000\nFT\n6.5\nx\n2\nx\n1.9 Continuous\nlength\ne)\n100\nFT\n2.75\nx\n2\nx\n1.125 Continuous\nlength\n \n[Spool\ndimensions may\nvary\nslightly]\nNotes:\na) Certain color\nand put-up combinations may only be\navailable by\nspecial order. Minimums may\napply.\nb)\nTolerance on 32800\nft. Put-Up is +/-\n10%</td>
</tr>
</tbody>
</table>

View File

@ -1,2 +0,0 @@
drop_off_tray(robot, 0)

44
test.py
View File

@ -1,4 +1,46 @@
print("\u001b[37m")
from pyModbusTCP.client import ModbusClient
def get_sensors():
mbconn = ModbusClient(host="192.168.1.20", port=502, auto_open=True, auto_close=True)
"""
port 1: 256
port 2: 272
port 3: 288
port 4: 304
port 5: 320
port 6: 336
port 7: 352
port 8: 368
"""
out = list()
for reg in [352, 288, 304, 368]:
val = mbconn.read_holding_registers(reg)[0] # read only one register
print(val)
if val == 1:
out.append(True)
else:
out.append(False)
return out
def get_open_spot(sensordata):
for x in range(len(sensordata)):
sens = sensordata[x]
if not sens:
return x
# if we get here, every spot is full
return False
testmb = get_sensors()
print(testmb)
print("Spot open", get_open_spot(testmb))
exit()
class Ring:
def __init__(self) -> None:

View File

@ -10,7 +10,7 @@ import yaml
import sys
from util import fprint
from pyModbusTCP.client import ModbusClient
from multiprocessing import Queue
import subprocess
from util import win32
@ -20,6 +20,7 @@ class Rob():
robot = None
#offset_x, offset_y, offset_z = (0, 0, 0.14) # Tool offset
#
def __init__(self, config):
self.config = config
armc = config["arm"]
@ -32,6 +33,7 @@ class Rob():
self.limb2 = limbs["limb2"]
self.limb3 = limbs["limb3"]
self.limb_wrist = limbs["limb_wrist"]
#self.init_arm()
def ping(host):
@ -59,9 +61,11 @@ def powerup_arm(robot):
#
# wait for power up (this function runs async)
while not ping(robot.ip):
count = 0
while not ping(robot.ip) and count == 10:
time.sleep(0.5)
count += 1
# trigger auto-initialize
fprint("Arm online. Waiting for calibration.")
# wait for auto-initialize
@ -72,21 +76,23 @@ def connect(robot):
ip = robot.ip
fprint("Connecting to arm at " + ip)
trying = True
while trying:
count = 0
while trying and count < 10:
count += 1
try:
robot.robot = urx.Robot(ip)
trying = False
except:
time.sleep(1)
# Sets robot arm endpoint offset (x,y,z,rx,ry,rz)
robot.robot = urx.Robot(ip, use_rt=False)
robot.robot.set_tcp((robot.offset_x, robot.offset_y, robot.offset_z, 0, 0, 0))
# Set weight
robot.robot.set_payload(2, (0, 0, 0.1))
trying = False
except:
time.sleep(0.5)
# Sets robot arm endpoint offset (x,y,z,rx,ry,rz)
return robot
def init_arm(robot):
robot = connect(robot)
# init urx
#rob.set_payload(2, (0, 0, 0.1))
@ -101,7 +107,9 @@ def init_arm(robot):
new_pos = curr_pos
new_pos[2] += 0.025
rob.movel(new_pos, vel=0.05, acc=1)
curr_j = rob.getj()
curr_j[3] -= 0.2 # radians
rob.movej(curr_j, vel=0.2, acc=1)
move_to_home(robot, speed=0.5)
return True
@ -253,11 +261,12 @@ def move_to_home(robot, keep_flip=False, speed=2):
flip(robot)
# Move robot to home position
rob.movej(offset_gripper_angle(robot, *(-0.18, -0.108, 0.35), flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position
rob.movej(offset_gripper_angle(robot, *(-0.18, -0.108, 0.25), flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position
return True
def move_to_packup(robot, speed=0.25):
robot = connect(robot)
rob = robot.robot
# known good starting point to reach store position
@ -486,10 +495,10 @@ def safe_move(robot, x, y, z, use_closest_path=True):
if (r <= flip_radius and is_flipped(robot)) or (r > flip_radius and not is_flipped(robot)):
flip(robot)
rob.movej(offset_gripper_angle(robot, x, y, z, flip=is_flipped(robot), use_closest_path=use_closest_path), vel=4, acc=3)
rob.movej(offset_gripper_angle(robot, x, y, z, flip=is_flipped(robot), use_closest_path=use_closest_path), vel=2, acc=2)
return True
def holder_routine(robot, holder_index, pick_up, verbose=False):
def holder_routine(robot, pos_updates, holder_index, pick_up, verbose=False):
robot = connect(robot)
rob = robot.robot
@ -514,7 +523,9 @@ def holder_routine(robot, holder_index, pick_up, verbose=False):
new_pos = curr_pos
new_pos[2] = 0.005
rob.movel(new_pos, vel=0.1, acc=1)
if pos_updates is not None:
pos_updates.put(1)
fprint("Triggering LED interface")
# Pick up or drop off
if pick_up:
close_gripper()
@ -525,12 +536,14 @@ def holder_routine(robot, holder_index, pick_up, verbose=False):
new_pos[2] = 0.2
rob.movel(new_pos, vel=2, acc=1)
was_flipped = is_flipped(robot)
if pos_updates is not None:
pos_updates.put(2)
fprint("Triggering LED interface")
# goto_holder_index(robot, 25, z=0.2)
def pick_up_holder(robot, holder_index, verbose=False):
holder_routine(robot, holder_index, True, verbose=verbose)
def drop_off_holder(robot, holder_index, verbose=False):
holder_routine(robot, holder_index, False, verbose=verbose)
def pick_up_holder(robot, pos_updates, holder_index, verbose=False):
holder_routine(robot, pos_updates, holder_index, True, verbose=verbose)
def drop_off_holder(robot, pos_updates, holder_index, verbose=False):
holder_routine(robot, pos_updates, holder_index, False, verbose=verbose)
def tray_routine(robot, slot=0, pick_up=True):
robot = connect(robot)
@ -568,14 +581,16 @@ def tray_routine(robot, slot=0, pick_up=True):
# Positions for each slot
slot_distance = .052
slot_height = -.015-.0095
first_slot = -0.3084+0.02
slot_height = -.015-.0095+0.007 # add 7mm for shim
first_slot = -0.3084+0.01+0.003 # add 3mm for tray adjust
slot_position = [
[first_slot, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+2*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+3*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
]
if pick_up:
open_gripper()
rob.movel(slot_position[slot], vel=0.2, acc=1)
# Place/Grab the tube
@ -608,6 +623,7 @@ def drop_off_tray(robot, slot=0):
def return_routine(robot, slot, holder_index=None, verbose=False):
# OLD UNUSED
robot = connect(robot)
rob = robot.robot
@ -641,29 +657,64 @@ def return_routine(robot, slot, holder_index=None, verbose=False):
return True
def goto_camera(robot):
def goto_camera(robot, pos_updates):
robot = connect(robot)
goto_holder_index(robot, 49, 0.2)
def tray_to_camera(robot, slot):
def tray_to_camera(robot, pos_updates, slot):
pick_up_tray(robot, slot)
goto_camera(robot)
def holder_to_tray(robot, holder_index, slot):
pick_up_holder(robot, holder_index)
goto_camera(robot, pos_updates)
def holder_to_tray(robot, pos_updates, holder_index, slot):
pick_up_holder(robot, pos_updates, holder_index)
drop_off_tray(robot, slot)
def holder_to_camera(robot, holder_index, verbose=False):
def holder_to_camera(robot, pos_updates, holder_index, verbose=False):
robot = connect(robot)
fprint("Bringing tube at " + str(holder_index) + " to camera")
rob = robot.robot
pick_up_holder(robot, holder_index)
goto_camera(robot)
def camera_to_holder(robot, holder_index, verbose=False):
pick_up_holder(robot, pos_updates, holder_index)
goto_camera(robot, pos_updates)
def camera_to_holder(robot, pos_updates, holder_index, verbose=False):
robot = connect(robot)
rob = robot.robot
drop_off_holder(robot, holder_index)
drop_off_holder(robot, pos_updates, holder_index)
# def open_gripper():
# fprint("Opening gripper")
# c = ModbusClient(host="192.168.1.21", port=502, auto_open=False, auto_close=False)
# c.open()
# while not c.is_open:
# time.sleep(0.01)
# c.write_single_register(112, 0b0)
# # c.write_single_register(435, 0b10000000)
# time.sleep(0.5)
# # c.write_single_register(112, 0b0)
# c.write_single_register(435, 0b10000000)
# time.sleep(0.5)
# c.close()
# #c.close()
# def close_gripper():
# fprint("Closing gripper")
# c = ModbusClient(host="192.168.1.21", port=502, auto_open=False, auto_close=False)
# c.open()
# while not c.is_open:
# time.sleep(0.01)
# c.write_single_register(435, 0b00000000)
# # c.write_single_register(112, 0b1)
# time.sleep(0.5)
# # c.write_single_register(435, 0b00000000)
# c.write_single_register(112, 0b1)
# time.sleep(0.5)
# c.close()
# time.sleep(0.2)
# #
def open_gripper():
@ -692,9 +743,46 @@ def close_gripper():
c.close()
#
def get_position_thread(robot, pos_updates):
try:
robot = connect(robot)
rob = robot.robot
oldvals = rob.getl()
deltavals = [0,0,0]
import uptime
t = 0.01
count = 0
while True:
start = uptime.uptime()
if pos_updates.qsize() < 2:
vals = rob.getl()
if vals != oldvals:
if pos_updates is not None:
pos_updates.put(tuple(oldvals))
#time.sleep(0.01)
# deltavals = list()
# deltavals.append(vals[0]-oldvals[0])
# deltavals.append(vals[1]-oldvals[1])
# deltavals.append(vals[2]-oldvals[2])
# count = 0
oldvals = vals
# else:
# count += 0.2
# if count < 1:
# tmpvals = vals
# tmpvals[0] = oldvals[0] + deltavals[0]*count
# tmpvals[1] = oldvals[1] + deltavals[1]*count
# tmpvals[2] = oldvals[2] + deltavals[2]*count
# pos_updates.put(tuple(tmpvals))
while start + t > uptime.uptime():
time.sleep(0.0001)
except:
pass
if __name__ == "__main__":
#init("192.168.1.145")
with open('config.yml', 'r') as fileread:
#global config
config = yaml.safe_load(fileread)
@ -710,16 +798,29 @@ if __name__ == "__main__":
#move_to_packup(robot)
move_to_home(robot)
pick_up_holder(robot, None, 8)
#drop_off_tray(robot, 0)
# drop_off_tray(robot, 1)
# drop_off_tray(robot, 2)
# drop_off_tray(robot, 3)
# pick_up_tray(robot, 1)
# drop_off_holder(robot, 5)
# pick_up_holder(robot, 26)
# drop_off_tray(robot, 3)
# for i in range(3):
# pick_up_holder(robot, i)
# for i in range(0,54):
# pick_up_holder(robot, None, i)
# print('Drop off', i+1)
# drop_off_holder(robot, i+1)
# #print('Drop off', i+1)
# drop_off_tray(robot, 0)
# #input()
holder_to_camera(robot, 0)
camera_to_holder(robot, 0)
# # holder_to_camera(robot, 0)
# # camera_to_holder(robot, 0)
print("Current tool pose is: ", rob.getl())
curr_pos = rob.getl()

View File

@ -26,8 +26,8 @@ if win32:
#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)
# _, username = res.strip().rsplit("\n", 1)
# userid, sysdom = username.rsplit("\\", 1)
if linux or macos:
sysid = hex(uuid.getnode())

View File

@ -150,7 +150,7 @@
setInterval(updateClock, 100);
</script>
<iframe src="http://192.168.1.12:3000/d-solo/cdiqwmlr8c9ogf/sensors?orgId=1&refresh=5s&from=1714431825720&to=1714433625721&theme=light&panelId=7" width="450" height="200" frameborder="0"></iframe>
</body>
</html>