Compare commits

...

123 Commits

Author SHA1 Message Date
Cole Deck
86971e0db4 Restart VM to clear network leftovers 2024-08-20 09:18:04 -05:00
Cole Deck
f04a2a4bd4 remove pick count file 2024-08-20 09:04:56 -05:00
Cole Deck
5b7afac517 Update web 2024-08-20 09:04:56 -05:00
Cole Deck
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
Cole Deck
48e7c7b85f Update sensor code 2024-08-08 10:51:05 -05:00
Cole Deck
ea5daf8689 Switch to unicode encoding 2024-08-06 17:41:45 -05:00
Cole Deck
05baceb388 Fix logic 2024-08-06 17:36:31 -05:00
Cole Deck
43694ca97d Use alternate method 2024-08-06 17:33:22 -05:00
Cole Deck
3f731a4bd3 Use alternate method 2024-08-06 17:30:41 -05:00
Cole Deck
419498cae6 Include stderr 2024-08-06 17:26:12 -05:00
Cole Deck
c89fc86812 Fix stdout flush 2024-08-06 17:16:20 -05:00
Cole Deck
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
Cole Deck
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
Cole Deck
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
BlueOceanWave
c52fe167bf Merge branch 'main' of https://git.myitr.org/Jukebox/jukebox-software 2024-04-26 17:49:14 -05:00
BlueOceanWave
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
BlueOceanWave
275cbd027e Added open gripper to init routine 2024-04-24 14:42:07 -05:00
BlueOceanWave
f4e43f33d2 Created functions for routines 2024-04-24 14:40:38 -05:00
BlueOceanWave
ae97ed1a14 Made pickup and dropoff routines for tray and holder. Adjusted home position 2024-04-23 18:56:11 -05:00
BlueOceanWave
237319db14 Fixed drop off routine to not hit walls 2024-04-22 21:56:07 -05:00
a698c4a753 Add more routines 2024-04-18 20:33:55 -05:00
939474378a Add open and close gripper functions 2024-04-12 22:05:34 -05:00
145f51d08c Update IPs for LED system 2024-04-12 20:28:34 -05:00
BlueOceanWave
4973fc79be Merge branch 'main' of https://git.myitr.org/Jukebox/jukebox-software 2024-04-12 20:28:33 -05:00
BlueOceanWave
ff2269193b small dimensions fixes 2024-04-12 20:28:31 -05:00
5edd7f4592 Remove extraneous pass 2024-03-27 20:41:07 -05:00
4dd6f7649a Implement cable_search 2024-03-27 20:40:20 -05:00
dc1e568a96 Add basic cable_map implementation 2024-03-27 20:33:21 -05:00
1ec6d92cfa Deploy docker containers 2024-03-27 19:50:01 -05:00
e21ded46f1 Update juekbox-web 2024-03-27 19:47:17 -05:00
672507f498 Add jukebox web 2024-03-27 19:46:45 -05:00
64bb50f055 Add setup-alpine-vm.sh 2024-03-28 00:44:57 +00:00
5016b4e99f Update compose.yml 2024-03-28 00:44:35 +00:00
efbda23c38 Fix docker runtime support 2024-03-27 18:53:50 -05:00
19ce328596 Correctly parse results from get_specs 2024-03-27 17:49:02 -05:00
ad216f21fa Implement cable_details call 2024-03-26 18:42:01 -05:00
BlueOceanWave
6d6c2030a9 Merge branch 'main' of https://git.myitr.org/Jukebox/jukebox-software 2024-03-26 15:24:20 -05:00
BlueOceanWave
9893222335 Pick-up routine 2024-03-26 15:24:18 -05:00
82a52dea5a Add cables to meilisearch db 2024-03-26 15:09:26 -05:00
77fdc43fce Add missing robot pass-ins 2024-03-24 15:35:46 -05:00
3de59f5985 Convert ur5_control to class based (untested) 2024-03-24 15:31:58 -05:00
BlueOceanWave
6887fa943b Merge branch 'main' of https://git.myitr.org/Jukebox/jukebox-software 2024-03-24 13:50:17 -05:00
BlueOceanWave
2ec7906ee4 Basic move restrictions and flip routine 2024-03-23 15:47:10 -05:00
069d2175d9 Convert led code to class based for multithreading 2024-03-23 15:33:51 -05:00
37 changed files with 5517 additions and 1219 deletions

3
.gitignore vendored
View File

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

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "jukebox-web"]
path = jukebox-web
url = https://git.myitr.org/Jukebox/jukebox-web

View File

@ -3,11 +3,11 @@ FROM python:3.11-slim
# Get runtime dependencies # Get runtime dependencies
# glx for OpenCV, ghostscript for datasheet PDF rendering, zbar for barcode scanning, git for cloning repos # glx for OpenCV, ghostscript for datasheet PDF rendering, zbar for barcode scanning, git for cloning repos
RUN apt-get update && apt-get install -y libgl1-mesa-glx ghostscript libzbar0 git && apt-get clean && rm -rf /var/lib/apt/lists RUN apt-get update && apt-get install -y libgl1-mesa-glx ghostscript libzbar0 git && apt-get clean && rm -rf /var/lib/apt/lists
COPY *.py *.yml *.sh *.txt *.html static templates ./ COPY requirements.txt ./
#COPY config-server.yml config.yml #COPY config-server.yml config.yml
RUN pip3 install -r requirements.txt RUN pip3 install -r requirements.txt
COPY *.py *.yml *.sh *.txt *.html static templates ./
CMD ["python3", "run.py"] CMD ["sh", "-c", "python3 run.py"]
EXPOSE 5000 EXPOSE 5000
EXPOSE 8000 EXPOSE 8000
EXPOSE 9000 EXPOSE 9000

View File

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

13
compose-search-only.yml Normal file
View File

@ -0,0 +1,13 @@
services:
meilisearch:
image: "getmeili/meilisearch:v1.6.2"
ports:
- "7700:7700"
environment:
MEILI_MASTER_KEY: fluffybunnyrabbit
MEILI_NO_ANALYTICS: true
volumes:
- "meili_data:/meili_data"
volumes:
meili_data:

View File

@ -8,6 +8,23 @@ services:
MEILI_NO_ANALYTICS: true MEILI_NO_ANALYTICS: true
volumes: volumes:
- "meili_data:/meili_data" - "meili_data:/meili_data"
jukebox-software:
build: .
init: true
ports:
- "5000:5000"
- "8000:8000"
- "9000:9000"
environment:
- PYTHONUNBUFFERED=1
depends_on:
- meilisearch
jukebox-web:
build: jukebox-web
ports:
- "3000:3000"
volumes: volumes:
meili_data: meili_data:

View File

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

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,24 +30,24 @@ def query_search(partnum, source):
fprint("Searching for " + partnum) fprint("Searching for " + partnum)
if source == "Belden": if source == "Belden":
token_url = "https://www.belden.com/coveo/rest/token?t=" + str(int(time.time())) token_url = "https://www.belden.com/coveo/rest/token?t=" + str(int(time.time()))
with requests.get(token_url) as r:
out = json.loads(r.content)
token = out["token"]
search_url = "https://www.belden.com/coveo/rest/search"
# Ridiculous search parameters extracted from website. Do not touch
search_data = r"""{ "q": "{QUERY}", "sortCriteria": "relevancy", "numberOfResults": "250", "sortCriteria": "@catalogitemwebdisplaypriority ascending", "searchHub": "products-only-search", "pipeline": "Site Search", "maximumAge": "900000", "tab": "products-search", "locale": "en", "aq": "(NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B)) ((@syssource==\"website_001002_catalog_index-rg-nc-prod-sitecore-prod\" @catalogitemprimarycategorypublished==true)) ((@catalogitemregionavailable=Global) (@z95xlanguage==en))", "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": " [{\"field\":\"@contenttype\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[\"Products\"],\"queryOverride\":\"{QUERY}\",\"advancedQueryOverride\":\"(NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B)) ((((((((@z95xpath=3324AF2D58F64C0FB725521052F679D2 @z95xid<>3324AF2D58F64C0FB725521052F679D2) ((@z95xpath=C292F3A37B3A4E6BAB345DF87ADDE516 @z95xid<>C292F3A37B3A4E6BAB345DF87ADDE516) @z95xtemplate==E4EFEB787BDC4B1A908EFC64D56CB2A4)) OR ((@z95xpath=723501A864754FEEB8AE377E4C710271 @z95xid<>723501A864754FEEB8AE377E4C710271) ((@z95xpath=600114EAB0E5407A84AAA9F0985B6575 @z95xid<>600114EAB0E5407A84AAA9F0985B6575) @z95xtemplate==2BE4FD6B3B2C49EBBD9E1F6C92238B05))) OR (@syssource==\\"website_001002_catalog_index-rg-nc-prod-sitecore-prod\\" @catalogitemprimarycategorypublished==true)) OR ((@z95xpath=3324AF2D58F64C0FB725521052F679D2 @z95xid<>3324AF2D58F64C0FB725521052F679D2) @z95xpath<>C292F3A37B3A4E6BAB345DF87ADDE516)) OR @syssource==\\"website_001002_Category_index-rg-nc-prod-sitecore-prod\\") NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B))) ((@catalogitemregionavailable=Global) (@z95xlanguage==en) OR (@contenttype=(Blogs,Resources,Other)) (NOT @ez120xcludefromcoveo==1))\",\"constantQueryOverride\":\"((@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\\"))\"},{\"field\":\"@catalogitembrand\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@catalogitemenvironment\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@catalogitemregionalavailability\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@prez45xtez120xt\",\"maximumNumberOfValues\":5,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@tags\",\"maximumNumberOfValues\":4,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetassettype\",\"maximumNumberOfValues\":3,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetbrand\",\"maximumNumberOfValues\":3,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetmarket\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetsolution\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetsearchcontentpagetype\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]}]" }"""
search_data = search_data.replace(r"{QUERY}", partnum)
#"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": "" }'
#fprint(search_data)
#fprint(json.loads(search_data))
#search_data = '{ "q": "' + str(partnum) + '" }'
#fprint(search_data)
headers = headers = {
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json'
}
try: try:
with requests.get(token_url) as r:
out = json.loads(r.content)
token = out["token"]
search_url = "https://www.belden.com/coveo/rest/search"
# Ridiculous search parameters extracted from website. Do not touch
search_data = r"""{ "q": "{QUERY}", "sortCriteria": "relevancy", "numberOfResults": "250", "sortCriteria": "@catalogitemwebdisplaypriority ascending", "searchHub": "products-only-search", "pipeline": "Site Search", "maximumAge": "900000", "tab": "products-search", "locale": "en", "aq": "(NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B)) ((@syssource==\"website_001002_catalog_index-rg-nc-prod-sitecore-prod\" @catalogitemprimarycategorypublished==true)) ((@catalogitemregionavailable=Global) (@z95xlanguage==en))", "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": " [{\"field\":\"@contenttype\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[\"Products\"],\"queryOverride\":\"{QUERY}\",\"advancedQueryOverride\":\"(NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B)) ((((((((@z95xpath=3324AF2D58F64C0FB725521052F679D2 @z95xid<>3324AF2D58F64C0FB725521052F679D2) ((@z95xpath=C292F3A37B3A4E6BAB345DF87ADDE516 @z95xid<>C292F3A37B3A4E6BAB345DF87ADDE516) @z95xtemplate==E4EFEB787BDC4B1A908EFC64D56CB2A4)) OR ((@z95xpath=723501A864754FEEB8AE377E4C710271 @z95xid<>723501A864754FEEB8AE377E4C710271) ((@z95xpath=600114EAB0E5407A84AAA9F0985B6575 @z95xid<>600114EAB0E5407A84AAA9F0985B6575) @z95xtemplate==2BE4FD6B3B2C49EBBD9E1F6C92238B05))) OR (@syssource==\\"website_001002_catalog_index-rg-nc-prod-sitecore-prod\\" @catalogitemprimarycategorypublished==true)) OR ((@z95xpath=3324AF2D58F64C0FB725521052F679D2 @z95xid<>3324AF2D58F64C0FB725521052F679D2) @z95xpath<>C292F3A37B3A4E6BAB345DF87ADDE516)) OR @syssource==\\"website_001002_Category_index-rg-nc-prod-sitecore-prod\\") NOT @z95xtemplate==(ADB6CA4F03EF4F47B9AC9CE2BA53FF97,FE5DD82648C6436DB87A7C4210C7413B))) ((@catalogitemregionavailable=Global) (@z95xlanguage==en) OR (@contenttype=(Blogs,Resources,Other)) (NOT @ez120xcludefromcoveo==1))\",\"constantQueryOverride\":\"((@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\\"))\"},{\"field\":\"@catalogitembrand\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@catalogitemenvironment\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@catalogitemregionalavailability\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@prez45xtez120xt\",\"maximumNumberOfValues\":5,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@tags\",\"maximumNumberOfValues\":4,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetassettype\",\"maximumNumberOfValues\":3,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetbrand\",\"maximumNumberOfValues\":3,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetmarket\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetsolution\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]},{\"field\":\"@facetsearchcontentpagetype\",\"maximumNumberOfValues\":6,\"sortCriteria\":\"occurrences\",\"injectionDepth\":1000,\"completeFacetWithStandardValues\":true,\"allowedValues\":[]}]" }"""
search_data = search_data.replace(r"{QUERY}", partnum)
#"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": "" }'
#fprint(search_data)
#fprint(json.loads(search_data))
#search_data = '{ "q": "' + str(partnum) + '" }'
#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: with requests.post(search_url, headers=headers, data=search_data) as r:
a = r.text a = r.text
a = json.loads(a) a = json.loads(a)
@ -81,15 +81,20 @@ def query_search(partnum, source):
brand = a["results"][idx]["raw"]["catalogitembrand"] brand = a["results"][idx]["raw"]["catalogitembrand"]
desc = a["results"][idx]["raw"]["catalogitemlongdesc"] desc = a["results"][idx]["raw"]["catalogitemlongdesc"]
shortdesc = a["results"][idx]["raw"]["catalogitemshortdesc"] 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) #print(a, urlname, img, uri, dsurl)
out = dict() out = dict()
out["url"] = "https://www.belden.com/products/" + uri out["url"] = "https://www.belden.com/products/" + uri
out["datasheet"] = "https://catalog.belden.com/techdata/EN/" + dsid + "_techdata.pdf" out["datasheet"] = "https://catalog.belden.com/techdata/EN/" + dsid + "_techdata.pdf"
out["brand"] = brand out["brand"] = brand
out["name"] = shortdesc out["short_description"] = shortdesc
out["description"] = desc out["description"] = desc
out["application"] = app
out["category"] = category
out["image"] = "https://www.belden.com" + img out["image"] = "https://www.belden.com" + img
out["partnum"] = name out["partnum"] = name
#print(out) #print(out)
@ -159,8 +164,9 @@ def touch(path):
def get_multi(partnums, delay=0.25, dir="cables/", cache=True): 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") as bar: #with alive_bar(len(partnums) * 2, dual_line=True, calibrate=30, bar="classic2", spinner="classic", disable=True, file=sys.stdout) as bar:
failed = list() failed = list()
actualpartnums = list() actualpartnums = list()
def _try_download_datasheet(partnum, output_dir, dstype): # Guess datasheet URL def _try_download_datasheet(partnum, output_dir, dstype): # Guess datasheet URL
@ -188,7 +194,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
# and set chunk_size parameter to None. # and set chunk_size parameter to None.
#if chunk: #if chunk:
bartext = bartext + "." bartext = bartext + "."
bar.text = bartext # bar.text = bartext
f.write(chunk) f.write(chunk)
#fprint("") #fprint("")
return output_dir + "/datasheet.pdf" return output_dir + "/datasheet.pdf"
@ -217,7 +223,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
# and set chunk_size parameter to None. # and set chunk_size parameter to None.
#if chunk: #if chunk:
bartext = bartext + "." bartext = bartext + "."
bar.text = bartext # bar.text = bartext
f.write(chunk) f.write(chunk)
#fprint("") #fprint("")
return output_dir + "/datasheet.pdf" return output_dir + "/datasheet.pdf"
@ -244,7 +250,7 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
# and set chunk_size parameter to None. # and set chunk_size parameter to None.
#if chunk: #if chunk:
bartext = bartext + "." bartext = bartext + "."
bar.text = bartext # bar.text = bartext
f.write(chunk) f.write(chunk)
#fprint("") #fprint("")
return output_dir + "/part-hires." + url.split(".")[-1] return output_dir + "/part-hires." + url.split(".")[-1]
@ -253,31 +259,31 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
os.remove(partnum + "/datasheet.pdf") os.remove(partnum + "/datasheet.pdf")
sys.exit() 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) fprint("Using cached datasheet for " + partnum)
bar.text = "Using cached datasheet for " + partnum # bar.text = "Using cached datasheet for " + partnum
bar(skipped=True) # bar(skipped=True)
if not os.path.exists(output_dir + "/parsed"): if not os.path.exists(output_dir + "/parsed"):
fprint("Parsing Datasheet contents of " + partnum) fprint("Parsing Datasheet contents of " + partnum)
bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..." # 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) # bar(skipped=False)
return out return out
else: else:
fprint("Datasheet already parsed for " + partnum) fprint("Datasheet already parsed for " + partnum)
bar.text = "Datasheet already parsed for " + partnum + ".pdf" # bar.text = "Datasheet already parsed for " + partnum + ".pdf"
bar(skipped=True) # bar(skipped=True)
def __downloaded_datasheet(partnum, path, output_dir, dstype): def __downloaded_datasheet(partnum, path, output_dir, dstype, weburl, extra):
fprint("Downloaded " + path) fprint("Downloaded " + path)
bar.text = "Downloaded " + path # bar.text = "Downloaded " + path
bar(skipped=False) # bar(skipped=False)
fprint("Parsing Datasheet contents of " + partnum) fprint("Parsing Datasheet contents of " + partnum)
bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..." # 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) # bar(skipped=False)
return out return out
def run_search(partnum): def run_search(partnum):
@ -289,8 +295,9 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
partnum = partnum.replace("/","_") partnum = partnum.replace("/","_")
output_dir = dir + partnum output_dir = dir + partnum
path = output_dir + "/datasheet.pdf" path = output_dir + "/datasheet.pdf"
weburl = ":" + str(webport) + "/" + partnum + "/"
bartext = "Downloading files for part " + partnum bartext = "Downloading files for part " + partnum
bar.text = bartext # bar.text = bartext
partnum = oldpartnum.replace("_","/") partnum = oldpartnum.replace("_","/")
returnval = [partnum, dstype, False, False] returnval = [partnum, dstype, False, False]
if (not os.path.exists(output_dir + "/found_part_hires")) or not (os.path.exists(path) and os.path.getsize(path) > 1) or not cache: if (not os.path.exists(output_dir + "/found_part_hires")) or not (os.path.exists(path) and os.path.getsize(path) > 1) or not cache:
@ -300,53 +307,80 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
if search_result is not False: if search_result is not False:
# Download high resolution part image if available and needed # Download high resolution part image if available and needed
#oldpartnum = partnum #oldpartnum = partnum
partnum = search_result["partnum"] partnum = search_result["partnum"]
returnval = [partnum, dstype, False, False] returnval = [partnum, dstype, False, False]
output_dir = dir + partnum output_dir = dir + partnum
path = output_dir + "/datasheet.pdf" path = output_dir + "/datasheet.pdf"
bartext = "Downloading files for part " + partnum bartext = "Downloading files for part " + partnum
bar.text = bartext 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 not os.path.exists(output_dir + "/found_part_hires") or not cache:
if _download_image(search_result["image"], output_dir): if _download_image(search_result["image"], output_dir):
fprint("Downloaded hi-res part image for " + partnum) fprint("Downloaded hi-res part image for " + partnum)
returnval = [partnum, dstype, True, False] returnval = [partnum, dstype, True, False]
touch(output_dir + "/found_part_hires") if os.path.exists(output_dir + "/parsed"):
os.remove(output_dir + "/parsed")
touch(output_dir + "/found_part_hires")
else: else:
fprint("Using cached hi-res part image for " + partnum) fprint("Using cached hi-res part image for " + partnum)
# Download datasheet from provided URL if needed # Download datasheet from provided URL if needed
if os.path.exists(path) and os.path.getsize(path) > 1 and cache: 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] returnval = [partnum, dstype, True, out]
elif _download_datasheet(search_result["datasheet"], output_dir) is not False: 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] returnval = [partnum, dstype, True, out]
elif os.path.exists(path) and os.path.getsize(path) > 1 and cache: 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] 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 # 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: 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] returnval = [partnum, dstype, False, out]
# Failed to download with search or guess :( # Failed to download with search or guess :(
else: else:
return False return False
time.sleep(delay)
actualpartnums.append(returnval) actualpartnums.append(returnval)
return returnval return returnval
# We already have a hi-res image and the datasheet - perfect! # We already have a hi-res image and the datasheet - perfect!
else: else:
fprint("Using cached hi-res part image for " + partnum) 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] returnval = [partnum, dstype, False, out]
actualpartnums.append(returnval)
return True return True
for fullpartnum in partnums: for fullpartnum in partnums:
if fullpartnum is False:
actualpartnums.append(False)
continue
if fullpartnum[0:2] == "BL": # catalog.belden.com entry if fullpartnum[0:2] == "BL": # catalog.belden.com entry
partnum = fullpartnum[2:] partnum = fullpartnum[2:]
dstype = "Belden" dstype = "Belden"
@ -373,19 +407,19 @@ def get_multi(partnums, delay=0.25, dir="cables/", cache=True):
time.sleep(delay) time.sleep(delay)
if not success: if not success:
fprint("Failed to download datasheet for part " + partnum) fprint("Failed to download datasheet for part " + partnum)
bar.text = "Failed to download datasheet for part " + partnum # bar.text = "Failed to download datasheet for part " + partnum
failed.append((partnum, dstype)) failed.append((partnum, dstype))
bar(skipped=True) # bar(skipped=True)
bar(skipped=True) # bar(skipped=True)
time.sleep(delay)
if len(failed) > 0:
fprint("Failed to download:") if len(failed) > 0:
for partnum in failed: fprint("Failed to download:")
fprint(partnum[1] + " " + partnum[0]) for partnum in failed:
return False, actualpartnums # Go to manual review upload page fprint(partnum[1] + " " + partnum[0])
else: return False, actualpartnums # Go to manual review upload page
return True, actualpartnums # All cables downloaded; we are good to go else:
return True, actualpartnums # All cables downloaded; we are good to go
@ -408,7 +442,7 @@ if __name__ == "__main__":
# ] # ]
partnums = [ partnums = [
# Actual cables in Jukebox # Actual cables in Jukebox
"BL3092A",
"AW86104CY", "AW86104CY",
"AW3050", "AW3050",
"AW6714", "AW6714",
@ -438,8 +472,9 @@ if __name__ == "__main__":
"BL6300FE 009Q", "BL6300FE 009Q",
"BLRA500P 006Q", "BLRA500P 006Q",
]
# Some ones I picked, including some invalid ones # Some ones I picked, including some invalid ones
a = [
"BL10GXS12", "BL10GXS12",
"BLRST%205L-RKT%205L-949", "BLRST%205L-RKT%205L-949",
"BL10GXS13", "BL10GXS13",
@ -455,7 +490,7 @@ if __name__ == "__main__":
"BLFISX006W0", # datasheet only "BLFISX006W0", # datasheet only
"BLFISX00103", # invalid "BLFISX00103", # invalid
"BLC6D1100007" # invalid "BLC6D1100007" # invalid
] ]
#print(query_search("TT-SLG-024-HTNN", "Belden")) #print(query_search("TT-SLG-024-HTNN", "Belden"))
from label_generator import gen_label from label_generator import gen_label

File diff suppressed because one or more lines are too long

1
jukebox-web Submodule

@ -0,0 +1 @@
Subproject commit 03ed1f22e8752c9b3c8dd313612fd9de30c55ddb

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 import signal
from PIL import Image from PIL import Image
from label_image import generate_code from label_image import generate_code
from label_document import generate_pdf
def input_cable(): def input_cable():
@ -22,7 +23,7 @@ def input_cable():
print("Input part number:", inputnum) print("Input part number:", inputnum)
print("Searching databases for cables...") print("Searching databases for cables...")
# Search both AW and BL sites # 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("") print("")
if len(output) > 1: if len(output) > 1:
for i in output: for i in output:
@ -70,6 +71,8 @@ def gen_label(partnum, path="labels"):
img = generate_code(partnum) img = generate_code(partnum)
os.makedirs(path, exist_ok=True) os.makedirs(path, exist_ok=True)
img.save(path + "/" + partnum + ".png") img.save(path + "/" + partnum + ".png")
generate_pdf(path)
def delete_folder(path): def delete_folder(path):
# Check if the path is a directory # 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_io = "belden-logo-superhires.png"
png_image_pillow = Image.open(png_image_io) png_image_pillow = Image.open(png_image_io)
png_width, png_height = png_image_pillow.size 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 png_width, png_height = png_image_pillow.size
# paste belden logo first because it has a big border that would cover stuff up # 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 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)) #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:]) text_width = font.getlength(partnum[2:])
txtx = (int(width * 10) - text_width) / 2 txtx = (int(width * 10) - text_width) / 2
txty = (int(width * 10)) / 2 txty = (int(width * 7.5)) / 2
# draw part number text # draw part number text
draw.text((txtx,txty),partnum[2:], "black", font) 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) qrcode = segno.make('HTTPS://BLDN.APP/' + partnum,micro=False,boost_error=False,error="L",mask=3)
out = io.BytesIO() out = io.BytesIO()
qrx, _ = qrcode.symbol_size(1,0) 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) 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 return img

2064
label_template.pdf Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

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: 372 KiB

After

Width:  |  Height:  |  Size: 344 KiB

View File

@ -4,30 +4,55 @@ import cv2
import banner_ivu_export import banner_ivu_export
import numpy as np import numpy as np
from util import fprint from util import fprint
import requests
class qr_reader(): class qr_reader():
camera = None camera = None
def __init__(self, ip, port): 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): def read_qr(self, tries=1):
print("Trying " + str(tries) + " frames.") try:
for x in range(tries): response = requests.get(self.url, timeout=tries * 15)
try: response.raise_for_status() # Raise an error for bad status codes
imgtype, img = self.camera.read_img() print(response.text) # Or handle the response as needed
#fprint(imgtype) if len(response.text) < 8:
image_array = np.frombuffer(img, np.uint8) return False
img = cv2.imdecode(image_array, cv2.IMREAD_COLOR) return response.text
#cv2.imshow('Image', img) except requests.Timeout:
#cv2.waitKey(1) print(f'The request timed out after {tries * 15} seconds')
detect = cv2.QRCodeDetector() except requests.RequestException as e:
value, points, straight_qrcode = detect.detectAndDecode(img) print(f'An error occurred: {e}')
return value
except:
continue
return False
return False
class video_streamer(): class video_streamer():
camera = None camera = None
def __init__(self, ip, port): def __init__(self, ip, port):
@ -41,5 +66,7 @@ class video_streamer():
if __name__ == "__main__": if __name__ == "__main__":
test = qr_reader("192.168.1.125", 32200) test = qr_reader("192.168.1.125", 32200)
import time
while True: 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 os
import glob import glob
import sys import sys
from PIL import Image
import segno
def touch(path): def touch(path):
with open(path, 'a'): with open(path, 'a'):
@ -37,7 +39,7 @@ def extract_table_name(table_start, searchpage, reader, dstype, fallbackname):
ymax = table_start + 10 ymax = table_start + 10
elif dstype == "Alphawire": elif dstype == "Alphawire":
ymin = table_start - 5 ymin = table_start - 5
ymax = table_start + 10 ymax = table_start + 20
page = reader.pages[searchpage - 1] page = reader.pages[searchpage - 1]
parts = [] parts = []
def visitor_body(text, cm, tm, fontDict, fontSize): 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 return text_body
#fprint(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 = [] tables = []
# Extract table data # Extract table data
try: try:
if dstype == "Belden": 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']) 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": 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: except (OSError, RuntimeError) as e:
print(e) print(e)
if win32: if win32:
@ -95,11 +135,26 @@ def parse(filename, output_dir, partnum, dstype):
#fprint(table_start) #fprint(table_start)
text_body = extract_table_name(table_start, table.page, reader, dstype, n) text_body = extract_table_name(table_start, table.page, reader, dstype, n)
#print(text_body)
table_list[text_body] = table.df table_list[text_body] = table.df
#print(table_list[text_body])
if dstype == "Alphawire": 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") #table.to_html("table" + str(n) + ".html")
#fprint(table.df) #fprint(table.df)
@ -133,6 +188,27 @@ def parse(filename, output_dir, partnum, dstype):
with open(output_dir + "/brand.png", "wb") as fp: with open(output_dir + "/brand.png", "wb") as fp:
fp.write(image_file_object.data) fp.write(image_file_object.data)
count += 1 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 # Table parsing and reordring
tables = dict() tables = dict()
@ -141,6 +217,7 @@ def parse(filename, output_dir, partnum, dstype):
#print(table_list.keys()) #print(table_list.keys())
for table_name in table_list.keys(): for table_name in table_list.keys():
# determine shape: horizontal or vertical # determine shape: horizontal or vertical
#print(table_name)
table = table_list[table_name] table = table_list[table_name]
rows = table.shape[0] rows = table.shape[0]
cols = table.shape[1] 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: if dstype == "Alphawire" and table_name_2.find("\n") >= 0:
torename[table_name_2] = table_name_2[0:table_name_2.find("\n")] torename[table_name_2] = table_name_2[0:table_name_2.find("\n")]
if table_name_2.find(table.iloc[-1, 0]) >= 0: # 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 # # Name taken from table directly above - this table does not have a name
torename[table_name_2] = "Specs " + str(len(tables)) # 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 # #table_list["Specs " + str(len(tables))] = table_list[table_name_2] # rename table to arbitrary altername name
break # break
if vertical: if vertical:
out = dict() out = dict()
@ -199,9 +276,9 @@ def parse(filename, output_dir, partnum, dstype):
out[col_data[0].replace("\n", " ")] = col_data[1:] out[col_data[0].replace("\n", " ")] = col_data[1:]
tables[table_name] = out tables[table_name] = out
#print(out)
# multi-page table check, Alphawire # 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 # table continues from previous page or has name on previous page
thistbl = table_list_raw[table_name] thistbl = table_list_raw[table_name]
prevtbl = table_list_raw[previous_table] prevtbl = table_list_raw[previous_table]
@ -251,9 +328,6 @@ def parse(filename, output_dir, partnum, dstype):
#fprint(table_name) #fprint(table_name)
#fprint(previous_table) #fprint(previous_table)
main_key = previous_table main_key = previous_table
cont_key = table_name cont_key = table_name
#fprint(tables) #fprint(tables)
@ -267,15 +341,21 @@ def parse(filename, output_dir, partnum, dstype):
del tables[table_name] del tables[table_name]
else: else:
#print(tables)
#print(main_key)
#print(cont_key)
for key in tables[cont_key].keys(): for key in tables[cont_key].keys():
tables[main_key][key] = tables[cont_key][key] tables[main_key][key] = tables[cont_key][key]
del tables[table_name] del tables[table_name]
else:
previous_table = table_name previous_table = table_name
else:
previous_table = table_name
# remove & rename tables # remove & rename tables
#print(torename)
for table_name in torename.keys(): for table_name in torename.keys():
tables[torename[table_name]] = tables[table_name] tables[torename[str(table_name)]] = tables[str(table_name)]
del tables[table_name] del tables[table_name]
# remove multi-line values that occasionally squeak through # remove multi-line values that occasionally squeak through
def replace_newlines_in_dict(d): def replace_newlines_in_dict(d):
@ -291,15 +371,42 @@ def parse(filename, output_dir, partnum, dstype):
tables = replace_newlines_in_dict(tables) tables = replace_newlines_in_dict(tables)
# summary # summary
#print(tables)
output_table = dict() output_table = dict()
output_table["partnum"] = partnum output_table["partnum"] = partnum
id = str(uuid.uuid4()) id = str(uuid.uuid4())
output_table["id"] = id output_table["id"] = id
#output_table["position"] = id #output_table["position"] = id
#output_table["brand"] = brand if "brand" in extra:
output_table["fullspecs"] = tables output_table["brand"] = extra["brand"]
output_table["searchspecs"] = {"partnum": partnum, **flatten(tables)} 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 output_table["searchspecs"]["id"] = id
@ -308,16 +415,17 @@ def parse(filename, output_dir, partnum, dstype):
#print(output_table) #print(output_table)
#run_cmd("rm \"" + output_dir + "\"/*.json") # not reliable! #run_cmd("rm \"" + output_dir + "\"/*.json") # not reliable!
pattern = os.path.join(output_dir, '*.json') # pattern = os.path.join(output_dir, '*.json')
json_files = glob.glob(pattern) # json_files = glob.glob(pattern)
for file_path in json_files: # for file_path in json_files:
os.remove(file_path) # os.remove(file_path)
#print(f"Deleted {file_path}") #print(f"Deleted {file_path}")
with open(output_dir + "/search_" + output_table["searchspecs"]["id"] + ".json", 'w') as json_file: with open(output_dir + "/search.json", 'w') as json_file:
json.dump(output_table["searchspecs"], json_file) json.dump(output_table["searchspecs"], json_file)
with open(output_dir + "/specs_" + output_table["partnum"] + ".json", 'w') as json_file: with open(output_dir + "/specs.json", 'w') as json_file:
json.dump(output_table["fullspecs"], json_file) json.dump(output_table["fullspecs"], json_file)
fprint("Datasheet values parsed and saved for " + partnum)
#print(json.dumps(output_table, indent=2)) #print(json.dumps(output_table, indent=2))
touch(output_dir + "/parsed") # mark as parsed touch(output_dir + "/parsed") # mark as parsed
return True return True
@ -346,12 +454,20 @@ def flatten(tables):
fullkeyname = (table + ": " + keyname).replace(".","") fullkeyname = (table + ": " + keyname).replace(".","")
if type(tables[table][key]) is not tuple: if type(tables[table][key]) is not tuple:
out[fullkeyname] = convert_to_number(tables[table][key]) if len(tables[table][key]) > 0:
out[fullkeyname] = convert_to_number(tables[table][key])
#print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",") #print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",")
elif len(tables[table][key]) == 1: elif len(tables[table][key]) == 1:
out[fullkeyname] = convert_to_number(tables[table][key][0]) if len(tables[table][key][0]) > 0:
out[fullkeyname] = convert_to_number(tables[table][key][0])
#print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",") #print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",")
else:
tmp = []
for x in range(len(tables[table][key])):
if len(tables[table][key][x]) > 0:
tmp.append(tables[table][key][x].strip())
#out[fullkeyname + " " + str(x+1)] = convert_to_number(tables[table][key][x])
out[fullkeyname] = tmp
# if the item has at least two commas in it, split it # if the item has at least two commas in it, split it
if tables[table][key].count(',') > 0: if tables[table][key].count(',') > 0:
out[fullkeyname] = list(map(lambda x: x.strip(), tables[table][key].split(","))) out[fullkeyname] = list(map(lambda x: x.strip(), tables[table][key].split(",")))
@ -369,4 +485,4 @@ def flatten(tables):
if __name__ == "__main__": 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 # Runtime
camelot-py camelot-py[base]==0.9.0
opencv-python #opencv-python
pypdf2==2.12.1 pypdf2==2.12.1
alive-progress alive-progress
requests requests
math3d==4.0.0
git+https://github.com/Byeongdulee/python-urx.git git+https://github.com/Byeongdulee/python-urx.git
meilisearch meilisearch
pyyaml pyyaml
@ -16,12 +17,14 @@ numpy
scipy scipy
ipywidgets ipywidgets
pandas pandas
pyarrow #pyarrow
ghostscript ghostscript
pyzbar pyzbar
segno segno
pyModbusTCP
paho-mqtt
# Development # Development
matplotlib matplotlib
#cx_Freeze # uncomment if building label generator app #cx_Freeze # uncomment if building label generator app
# requires windows 10 SDK, visual C++, etc # requires windows 10 SDK, visual C++, etc

1158
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

@ -1,10 +1,12 @@
#!/usr/bin/env python3
"""Interactions with the Meilisearch API for adding and searching cables.""" """Interactions with the Meilisearch API for adding and searching cables."""
from meilisearch import Client from meilisearch import Client
from meilisearch.task import TaskInfo from meilisearch.task import TaskInfo
from meilisearch.errors import MeilisearchApiError from meilisearch.errors import MeilisearchApiError
import json import time
DEFAULT_URL = "http://localhost:7700" DEFAULT_URL = "http://127.0.0.1:7700"
DEFAULT_APIKEY = "fluffybunnyrabbit" # I WOULD RECOMMEND SOMETHING MORE SECURE DEFAULT_APIKEY = "fluffybunnyrabbit" # I WOULD RECOMMEND SOMETHING MORE SECURE
DEFAULT_INDEX = "cables" DEFAULT_INDEX = "cables"
DEFAULT_FILTERABLE_ATTRS = ["partnum", "uuid", "position"] # default filterable attributes DEFAULT_FILTERABLE_ATTRS = ["partnum", "uuid", "position"] # default filterable attributes
@ -34,12 +36,15 @@ class JukeboxSearch:
# create the index if it does not exist already # create the index if it does not exist already
try: try:
self.client.get_index(self.index) self.client.get_index(self.index)
self.client.delete_index(self.index)
self.client.create_index(self.index)
except MeilisearchApiError as _: except MeilisearchApiError as _:
self.client.create_index(self.index) self.client.create_index(self.index)
# make a variable to easily reference the index # make a variable to easily reference the index
self.idxref = self.client.index(self.index) self.idxref = self.client.index(self.index)
time.sleep(0.05)
# update filterable attributes if needed # update filterable attributes if needed
self.idxref.update_distinct_attribute('partnum')
self.update_filterables(filterable_attrs) self.update_filterables(filterable_attrs)
def add_document(self, document: dict) -> TaskInfo: def add_document(self, document: dict) -> TaskInfo:
@ -65,11 +70,10 @@ class JukeboxSearch:
:param filterables: List of all filterable attributes""" :param filterables: List of all filterable attributes"""
existing_filterables = self.idxref.get_filterable_attributes() #existing_filterables = self.idxref.get_filterable_attributes()
if len(set(existing_filterables).difference(set(filterables))) > 0: #if len(set(existing_filterables).difference(set(filterables))) > 0:
taskref = self.idxref.update_filterable_attributes(filterables) taskref = self.idxref.update_filterable_attributes(filterables)
#self.client.wait_for_task(taskref.index_uid)
self.client.wait_for_task(taskref.index_uid)
def search(self, query: str, filters: str = None): def search(self, query: str, filters: str = None):
"""Execute a search query on the Meilisearch index. """Execute a search query on the Meilisearch index.
@ -90,7 +94,7 @@ class JukeboxSearch:
:returns: A dict containing the results; If no results found, an empty dict.""" :returns: A dict containing the results; If no results found, an empty dict."""
q = self.search("", filter) q = self.search("", filter)
if q["estimatedTotalHits"] != 0: if q["estimatedTotalHits"] != 0:
return ["hits"][0] return q["hits"][0]
else: else:
return dict() return dict()
@ -114,4 +118,4 @@ class JukeboxSearch:
# entrypoint # entrypoint
if __name__ == "__main__": if __name__ == "__main__":
jbs = JukeboxSearch() jbs = JukeboxSearch()

View File

@ -83,7 +83,7 @@ async def send_messages(to_server_queue):
await asyncio.sleep(0.001) await asyncio.sleep(0.001)
def websocket_server(to_server_queue, from_server_queue): 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().run_until_complete(start_server)
asyncio.get_event_loop().create_task(send_messages(to_server_queue)) asyncio.get_event_loop().create_task(send_messages(to_server_queue))

20
setup-alpine-vm.sh Normal file
View File

@ -0,0 +1,20 @@
#!/bin/sh
# This script must run as root!
echo "https://dl-cdn.alpinelinux.org/alpine/latest-stable/main
https://dl-cdn.alpinelinux.org/alpine/latest-stable/community" > /etc/apk/repositories
apk upgrade
apk add git docker docker-cli-compose
rc-update add docker
service docker start
git clone https://git.myitr.org/Jukebox/jukebox-software
cd jukebox-software
git submodule init
git submodule update
docker compose build
docker compose up -d

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>

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: class Ring:
def __init__(self) -> None: def __init__(self) -> None:

View File

@ -5,54 +5,117 @@ import math
import numpy as np import numpy as np
import time import time
import os import os
import logging #import logging
from urx.robotiq_two_finger_gripper import Robotiq_Two_Finger_Gripper import yaml
import sys import sys
from util import fprint from util import fprint
from pyModbusTCP.client import ModbusClient
from multiprocessing import Queue
import subprocess
from util import win32
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"]
self.ip = armc["ip"]
tool = armc["tool"]
limbs = armc["limbs"]
self.offset_x, self.offset_y, self.offset_z = (tool["offset_x"], tool["offset_y"], tool["offset_z"])
self.limb_base = limbs["limb_base"]
self.limb1 = limbs["limb1"]
self.limb2 = limbs["limb2"]
self.limb3 = limbs["limb3"]
self.limb_wrist = limbs["limb_wrist"]
#self.init_arm()
rob = None def ping(host):
offset_x, offset_y, offset_z = (0, 0, 0.14) # Tool offset #Returns True if host (str) responds to a ping request.
limb_base, limb1, limb2, limb3, limb_wrist = (0.105, .425, .39225, .1, .0997) # Limb lengths
def init(ip): # Option for the number of packets as a function of
global rob 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 powerup_arm(robot):
#sys.stdout = Logger() #sys.stdout = Logger()
fprint("Starting UR5 power up...") fprint("Starting UR5 power up...")
# power up robot here via PCB
# power up robot here
#
# wait for power up (this function runs async) # wait for power up (this function runs async)
count = 0
while not ping(robot.ip) and count == 10:
time.sleep(0.5)
count += 1
# trigger auto-initialize # trigger auto-initialize
fprint("Arm online. Waiting for calibration.")
# wait for auto-initialize # wait for auto-initialize
def connect(robot):
if robot.robot is None:
newrobot = Rob(robot.config)
robot = newrobot
ip = robot.ip
fprint("Connecting to arm at " + ip)
trying = True
count = 0
while trying and count < 10:
count += 1
try:
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 # 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)) #rob.set_payload(2, (0, 0, 0.1))
time.sleep(0.2) time.sleep(0.2)
fprint("UR5 ready.") fprint("UR5 ready.")
#return robot.robot
def set_pos_abs(x, y, z, xb, yb, zb, threshold=None): # setup - in case of fail. open gripper, move up, then go home.
global rob rob = robot.robot
open_gripper()
curr_pos = rob.getl()
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
def set_pos_abs(robot, x, y, z, xb, yb, zb, threshold=None):
rob = robot.robot
new_orientation = m3d.Transform() new_orientation = m3d.Transform()
new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis 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_yb(yb) # Replace ry with the desired rotation around Y-axis
@ -68,8 +131,8 @@ def set_pos_abs(x, y, z, xb, yb, zb, threshold=None):
#rob.speedj(0.2, 0.5, 99999) #rob.speedj(0.2, 0.5, 99999)
rob.set_pose(new_trans, acc=2, vel=2, command="movej", threshold=threshold) # apply the new pose rob.set_pose(new_trans, acc=2, vel=2, command="movej", threshold=threshold) # apply the new pose
def set_pos_rel_rot_abs(x, y, z, xb, yb, zb): def set_pos_rel_rot_abs(robot, x, y, z, xb, yb, zb):
global rob rob = robot.robot
new_orientation = m3d.Transform() new_orientation = m3d.Transform()
new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis 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_yb(yb) # Replace ry with the desired rotation around Y-axis
@ -86,8 +149,8 @@ def set_pos_rel_rot_abs(x, y, z, xb, yb, zb):
#rob.speedj(0.2, 0.5, 99999) #rob.speedj(0.2, 0.5, 99999)
rob.set_pose(new_trans, acc=0.1, vel=0.4, command="movej") # apply the new pose rob.set_pose(new_trans, acc=0.1, vel=0.4, command="movej") # apply the new pose
def set_pos_abs_rot_rel(x, y, z, xb, yb, zb): def set_pos_abs_rot_rel(robot, x, y, z, xb, yb, zb):
global rob rob = robot.robot
new_orientation = m3d.Transform() new_orientation = m3d.Transform()
new_orientation.orient.rotate_xb(xb) # Replace rx with the desired rotation around X-axis 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_yb(yb) # Replace ry with the desired rotation around Y-axis
@ -132,9 +195,8 @@ def polar_to_cartesian(r, theta):
y = r * np.sin(theta) y = r * np.sin(theta)
return x, y return x, y
def move_to_polar(robot, start_pos, end_pos):
def move_to_polar(start_pos, end_pos): rob = robot.robot
global rob
# Convert to polar coordinates # Convert to polar coordinates
start_r, start_theta = cartesian_to_polar(start_pos[0], start_pos[1]) start_r, start_theta = cartesian_to_polar(start_pos[0], start_pos[1])
@ -191,24 +253,35 @@ def move_to_polar(start_pos, end_pos):
return rx_intermediate return rx_intermediate
def degtorad(angle):
return angle/180.0 * math.pi
def radtodeg(angle):
return angle*180.0 / math.pi
def move_to_home(): def move_to_home(robot, keep_flip=False, speed=2):
global rob rob = robot.robot
if is_flipped(robot) and not keep_flip:
flip(robot)
# Move robot to home 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
goto_holder_index(robot, 12, 0.3, flip=False, use_closest_path=False)
# Home position in degrees # Home position in degrees
home_pos = [0.10421807948612624, store_pos = [-1.5708,
-2.206111555015423, -1.3,
1.710679229503537, 2.362,
-1.075834511928354, 0.7056,
-1.569301366430687, -1.425,
1.675098295930943] 1.5708]
# Move robot # Move robot
rob.movej(home_pos, acc=2, vel=2) rob.movej(store_pos, acc=0.1, vel=speed)
return True
def normalize_degree(theta): def normalize_degree(theta):
# Normalizes degree theta from -1.5pi to 1.5pi # Normalizes degree theta from -1.5pi to 1.5pi
@ -222,15 +295,14 @@ def normalize_degree(theta):
# Return angle # Return angle
return normalized_theta return normalized_theta
def get_joints_from_xyz_rel(robot, x, y, z, rx=0, ry=-math.pi/2, rz=0, initial_guess = (math.pi/2, math.pi/2, 0), l3offset=0):
def get_joints_from_xyz_rel(x, y, z, rx=0, ry=-math.pi/2, rz=0, initial_guess = (math.pi/2, math.pi/2, 0), l3offset=0):
# Get limbs and offsets # Get limbs and offsets
#l3=0.15 #l3=0.15
l_bs, l1, l2, l3, l_wt = (limb_base, limb1, limb2, limb3, limb_wrist) # Limb lengths l_bs, l1, l2, l3, l_wt = (robot.limb_base, robot.limb1, robot.limb2, robot.limb3, robot.limb_wrist) # Limb lengths
l3 += l3offset # add wrist offset, used for gripper angle calculations l3 += l3offset # add wrist offset, used for gripper angle calculations
offset_x = robot.offset_x
offset_y = robot.offset_y
offset_z = robot.offset_z
# Calculate base angle and r relative to shoulder joint # Calculate base angle and r relative to shoulder joint
def calculate_theta(x, y, a): def calculate_theta(x, y, a):
# Calculate if we need the + or - in our equations # Calculate if we need the + or - in our equations
@ -273,200 +345,487 @@ def get_joints_from_xyz_rel(x, y, z, rx=0, ry=-math.pi/2, rz=0, initial_guess =
# Return result # Return result
return base, shoulder, elbow, wrist1, ry, rz return base, shoulder, elbow, wrist1, ry, rz
def get_joints_from_xyz_abs(x, y, z, rx=0, ry=-math.pi/2, rz=math.pi/2, l3offset=0): def get_joints_from_xyz_abs(robot, x, y, z, rx=0, ry=-math.pi/2, rz=math.pi/2, l3offset=0, use_closest_path=True):
joints = get_joints_from_xyz_rel(x, y, z, rx, ry, rz, l3offset=l3offset) rob = robot.robot
joints = get_joints_from_xyz_rel(robot, x, y, z, rx, ry, rz, l3offset=l3offset)
# Return current positions if coordinates don't make sense
if z<0:
return rob.getj()
# Joint offsets # Joint offsets
# Base, Shoulder, Elbow, Wrist # Base, Shoulder, Elbow, Wrist
inverse = [1, -1, 1, 1, 1, 1] inverse = [1, -1, 1, 1, 1, 1]
offsets = [-math.pi/2, 0, 0, -math.pi/2, 0, 0] offsets = [-math.pi/2, 0, 0, -math.pi/2, 0, 0]
if radtodeg(joints[1]) > 137:
if math.degrees(joints[1]) > 137:
print("CRASH! Shoulder at", joints[1] * 180/math.pi) print("CRASH! Shoulder at", joints[1] * 180/math.pi)
#else: #else:
#print("Shoulder at", joints[1] * 180/math.pi) #print("Shoulder at", joints[1] * 180/math.pi)
# Return adjusted joint positions
return [o+j*i for j, o, i in zip(joints, offsets, inverse)]
# gripper angle: from vertical # Get adjusted joint positions
# gripper length: from joint to start of grip adjusted_joints = [o+j*i for j, o, i in zip(joints, offsets, inverse)]
# to flip, you can use flip=True or make gripper angle negative
def offset_gripper_angle(x, y, z, gripperangle=35, gripperlength=0.20+0.018, flip=False): curr_joints = rob.getj()
def get_complimentary_angle(joint_angle):
if joint_angle<0:
new_angle = joint_angle + 2*math.pi
else:
new_angle = joint_angle - 2*math.pi
if abs(new_angle) > math.radians(350):
return joint_angle
else:
return new_angle
# Use closest path (potentially going beyond 180 degrees)
if use_closest_path:
if abs(get_complimentary_angle(adjusted_joints[0])-curr_joints[0]) < abs(adjusted_joints[0]-curr_joints[0]):
adjusted_joints[0] = get_complimentary_angle(adjusted_joints[0])
# final_joint_positions = []
# for curr_joint, adjusted_joint in zip(curr_joints, adjusted_joints):
# if abs(curr_joint - adjusted_joint) < abs(curr_joint - get_complimentary_angle(adjusted_joint)):
# final_joint_positions.append(adjusted_joint)
# else:
# final_joint_positions.append(get_complimentary_angle(adjusted_joint))
# return final_joint_positions
return adjusted_joints
def move_arc(robot, x, y, z, rx=0, ry=-math.pi/2, rz=math.pi/2):
rob = robot.robot
start_joints = rob.getj()
end_joint = get_joints_from_xyz_abs(robot, x, y, z, rx, ry, rz)
n_points = 50
intermediate_joints = []
for i in range(0, 6):
intermediate_joints.append(np.linspace(start_joints[i], end_joint[i], n_points))
joints = [joint_position for joint_position in zip(*intermediate_joints)]
rob.movejs(joints, acc=2, vel=2, radius=0.1)
def offset_gripper_angle(robot, x, y, z, gripperangle=30, gripperlength=0.20+0.018, flip=False, use_closest_path=True, rzoffset=0):
# gripper angle: from vertical
# gripper length: from joint to start of grip
# to flip, you can use flip=True or make gripper angle negative
limb3 = robot.limb3
# Determine tool rotation depending on gripper angle
if gripperangle < 0: if gripperangle < 0:
rz = - math.pi / 2 rz = - math.pi / 2
else: else:
rz = math.pi / 2 rz = math.pi / 2
if flip: if flip:
gripperangle = -degtorad(gripperangle) gripperangle = -math.radians(gripperangle)
grippery = gripperlength - math.cos(gripperangle) * gripperlength grippery = gripperlength - math.cos(gripperangle) * gripperlength
grippery += math.sin(gripperangle) * limb3 grippery += math.sin(gripperangle) * limb3
gripperx = math.sin(gripperangle) * gripperlength + limb3 * 2 gripperx = math.sin(gripperangle) * gripperlength + limb3 * 2
gripperx -= (1-math.cos(gripperangle)) * limb3 gripperx -= (1-math.cos(gripperangle)) * limb3
rz = math.pi / 2 rz = math.pi / 2
# flip the whole wrist # flip the whole wrist
return get_joints_from_xyz_abs(x, y, z-grippery, rx=gripperangle + degtorad(180), l3offset=-gripperx, ry=math.pi/2, rz=rz) return get_joints_from_xyz_abs(robot, x, y, z-grippery, rx=gripperangle + math.radians(180), l3offset=-gripperx, ry=-3*math.pi/2, rz=rz + rzoffset, use_closest_path=use_closest_path)
else: else:
gripperangle = degtorad(gripperangle) gripperangle = math.radians(gripperangle)
grippery = gripperlength - math.cos(gripperangle) * gripperlength grippery = gripperlength - math.cos(gripperangle) * gripperlength
grippery -= math.sin(gripperangle) * limb3 grippery -= math.sin(gripperangle) * limb3
gripperx = math.sin(gripperangle) * gripperlength gripperx = math.sin(gripperangle) * gripperlength
gripperx += (1-math.cos(gripperangle)) * limb3 gripperx += (1-math.cos(gripperangle)) * limb3
return get_joints_from_xyz_abs(x, y, z-grippery, rx=gripperangle, l3offset=-gripperx, rz=rz) return get_joints_from_xyz_abs(robot, x, y, z-grippery, rx=gripperangle, l3offset=-gripperx, rz=rz, use_closest_path=use_closest_path)
def goto_holder_index(robot, idx, z=0.05, gripperangle=30, flip=False, use_closest_path=True, verbose=False):
joint = robot.config["position_map"][idx]
if verbose:
print("Going to cable holder index", joint["index"], "at position", joint["pos"])
safe_move(robot, joint["pos"][0]/1000, joint["pos"][1]/1000, z, use_closest_path=use_closest_path)
#angles = offset_gripper_angle(joint["pos"][1]/1000, joint["pos"][0]/1000, z, gripperangle=gripperangle, flip=flip)
#rob.movej(angles, acc=2, vel=2)
#return angles
#angles = get_joints_from_xyz_abs(joint["pos"][1]/1000, joint["pos"][0]/1000, 0.05, )
return True
def is_flipped(robot):
rob = robot.robot
wrist1 = rob.getj()[3]
if wrist1 > 0:
return True
else:
return False
def flip(robot):
rob = robot.robot
# A list of safe positions to flip
safe_positions = [(-0.18, -0.108, 0.35),
(0.18, -0.108, 0.35)]
def goto_holder_index(idx, z=0.05, gripperangle=35, flip=False): # Find the closest safe position
joint = config["position_map"][idx] curr_pos = rob.getl()[:3]
print("Going to cable holder index", joint["index"], "at position", joint["pos"]) def dist_from_robot(pos):
angles = offset_gripper_angle(joint["pos"][1]/1000, joint["pos"][0]/1000, z, gripperangle=gripperangle, flip=flip) x, y, z = pos
#rob.movej(angles, acc=2, vel=2) rx, ry, rz = curr_pos
return angles return math.sqrt((rx-x)**2+(ry-y)**2+(rz-z)**2)
#angles = get_joints_from_xyz_abs(joint["pos"][1]/1000, joint["pos"][0]/1000, 0.05, )
pos_dist_pairs = zip(safe_positions, [dist_from_robot(pos) for pos in safe_positions])
safe_pos = min(pos_dist_pairs, key=lambda x:x[1])[0]
# Flip at safe position
rob.movej(offset_gripper_angle(robot, *safe_pos, flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position
rob.movej(offset_gripper_angle(robot, *safe_pos, flip=(not is_flipped(robot))), vel=2, acc=2) # Flip gripper
# print('flip?: ', is_flipped(robot))
return True
def safe_move(robot, x, y, z, use_closest_path=True):
rob = robot.robot
flip_radius = 0.22 # Min radius on which to flip
r = math.sqrt(x**2 + y**2) # Get position radius
# Flip gripper if needed
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=2, acc=2)
return True
def holder_routine(robot, pos_updates, holder_index, pick_up, verbose=False):
robot = connect(robot)
rob = robot.robot
# Don't attempt to place a tube in the camera slot
if holder_index == 49:
return
if verbose:
fprint('Pickup routine for index' + str(holder_index))
# Go to the correct holder
if pick_up:
goto_holder_index(robot, holder_index, 0.05, use_closest_path=False)
else:
goto_holder_index(robot, holder_index, 0.2, use_closest_path=False)
if pick_up:
open_gripper()
# Move down
curr_pos = rob.getl()
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()
else:
open_gripper()
# Move up
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, 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)
rob = robot.robot
# Default to 0 if invalid value
if slot not in [0, 1, 2, 3]:
slot = 0
slot_prepositions = [(-9.93, -112.67, 144.02, -116.69, -54.13, -10.29),
(-12.35, -124.95, 148.61, -107.27, -54.36, -13.26),
(-16.45, -96.97, 137.85, 58.39, -305.08, 161.75),
(-16.66, -97.28, 138.16, 58.54, -305.05, 161.50)]
# Initial position depending on slot and robot orientation
if slot in [0, 1]:
if is_flipped(robot):
flip(robot)
else:
move_to_home(robot, keep_flip=True)
else:
goto_holder_index(robot, 25, z=0.3)
# Align robot to the slot
if slot in [2,3]:
angles = [(-2.77, -99.64, 131.02, 67.67, 70.04-360, 153.03),
slot_prepositions[slot]]
else:
angles = [(-58, -114.45, 100.52, -45.24, -96.95, 120),
(-39.98, -124.92, 132.28, -61.56, -55.60, -50.77),
slot_prepositions[slot]]
angles = [[x*math.pi/180 for x in move] for move in angles]
rob.movejs(angles,vel=2,acc=1)
# Positions for each slot
slot_distance = .052
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
if pick_up:
close_gripper()
else:
open_gripper()
# Move back
tilt = 0.3
curr_pos = rob.getl()
new_pos = curr_pos
if slot==3:
new_pos[0] -= 0.05 #x
new_pos[1] += 0.15 #y
new_pos[2] = 0.09 #z
new_pos[3] += tilt
new_pos[4] += tilt
new_pos[5] += tilt
rob.movel(new_pos, vel=0.2, acc=1)
# Go home to safe position
move_to_home(robot, speed=1, keep_flip=True)
def pick_up_tray(robot, slot=0):
tray_routine(robot, slot, True)
def drop_off_tray(robot, slot=0):
tray_routine(robot, slot, False)
def return_routine(robot, slot, holder_index=None, verbose=False):
# OLD UNUSED
robot = connect(robot)
rob = robot.robot
open_gripper()
was_flipped = is_flipped(robot)
if slot is None:
rob.movej(offset_gripper_angle(robot, -0.15, -0.15, 0.3, flip=was_flipped, use_closest_path=False), vel=4, acc=3)
rob.movej(get_joints_from_xyz_abs(robot, -0.35, -0.15, 0.0, math.pi/2, 0.1), vel=4, acc=3)
close_gripper()
else:
xoffset = 0.051 * slot
rob.movej(offset_gripper_angle(robot, -0.15, -0.15, 0.3, flip=was_flipped, use_closest_path=False), vel=4, acc=3)
rob.movej(get_joints_from_xyz_abs(robot, -0.35+xoffset, -0.15, 0.0, math.pi/2, 0.1), vel=4, acc=3)
close_gripper()
if holder_index is not None:
goto_holder_index(robot, holder_index, 0.2, use_closest_path=False)
curr_pos = rob.getl()
new_pos = curr_pos
new_pos[2] = 0.015
rob.movel(new_pos, vel=0.1, acc=1)
open_gripper()
new_pos[2] = 0.1
rob.movel(new_pos, vel=2, acc=1)
return True
else:
# go to camera
rob.movej(offset_gripper_angle(robot, 0.35, -0.35, 0.3, flip=was_flipped, use_closest_path=False), vel=2, acc=2)
return True
def goto_camera(robot, pos_updates):
robot = connect(robot)
goto_holder_index(robot, 49, 0.2)
def tray_to_camera(robot, pos_updates, slot):
pick_up_tray(robot, slot)
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, 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, 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, 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():
fprint("Opening gripper")
c = ModbusClient(host="192.168.1.21", port=502, auto_open=True, auto_close=False)
c.write_single_register(112, 0b0)
c.write_single_register(435, 0b10000000)
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=True, auto_close=False)
c.write_single_register(435, 0b00000000)
c.write_single_register(112, 0b1)
c.write_single_register(435, 0b00000000)
c.write_single_register(112, 0b1)
time.sleep(0.5)
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__": 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")
print("Current tool pose is: ", rob.getl())
move_to_home()
home_pose = [-0.4999999077032916,
-0.2000072960336574,
0.40002172976662786,
0,
-3.14152741295329,
math.radians(62)]
# time.sleep(.5)
p1 = [0,
0.6,
.4,
0.2226,
3.1126,
0.0510]
p2 = [0.171,
-0.115,
0.2,
0.2226,
3.1126,
0.0510]
curr_pos = rob.getl()
# up/down,
# tool rotation
# tool angle (shouldn't need)
# rob.set_pos(p1[0:3], acc=0.5, vel=0.5)
# set_pos_abs(*home_pose)
# angles = get_joints_from_xyz_abs(-0.2, 0, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(-0.2, -0.2, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.6, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.5, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.4, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.3, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.2, 0)
# rob.movej(angles, acc=2, vel=2)
# angles = get_joints_from_xyz_abs(0, -0.13, 0)
# rob.movej(angles, acc=2, vel=2)
config = None
joints = []
for i in np.linspace(-0.2, -0.7, 50):
joints.append(get_joints_from_xyz_abs(i, 0, 0))
#rob.movejs(joints, acc=2, vel=2)
import yaml
with open('config.yml', 'r') as fileread: with open('config.yml', 'r') as fileread:
#global config #global config
config = yaml.safe_load(fileread) config = yaml.safe_load(fileread)
#rob.movej(goto_holder_index(24, 0.2, 0), acc=2, vel=2)
#joints = []
#for i in np.linspace(0, 340, 340):
# joints.append(goto_holder_index(24, 0.5, i))
#rob.movejs(joints, acc=1, vel=3)
angle = 30
rob.movej(goto_holder_index(26, 0.1, angle), acc=2, vel=2)
time.sleep(1)
rob.movej(goto_holder_index(25, 0.1, angle), acc=2, vel=2)
time.sleep(1)
rob.movej(goto_holder_index(24, 0.1, angle, flip=True), acc=2, vel=2)
#rob.movej(goto_holder_index(32, 0.2, angle), acc=2, vel=2)
#rob.movej(goto_holder_index(38, 0.2, angle), acc=2, vel=2)
#rob.movej(goto_holder_index(25, 0.1, angle, flip=True), acc=2, vel=2)
#rob.movej(goto_holder_index(25, 0.2, angle, flip=True), acc=2, vel=2)
#rob.movej(goto_holder_index(24, 0.1, angle, flip=True), acc=2, vel=2)
#time.sleep(1)
#rob.movej(goto_holder_index(25, 0.1, angle, flip=True), acc=2, vel=2)
#rob.movej(goto_holder_index(49, 0.1, angle), acc=2, vel=2)
#rob.movej(goto_holder_index(49, 0.1, angle, flip=True), acc=2, vel=2)
# rob.movej(goto_holder_index(50, 0.1, angle, flip=True), acc=2, vel=2)
# rob.movej(goto_holder_index(51, 0.1, angle, flip=True), acc=2, vel=2)
# rob.movej(goto_holder_index(52, 0.1, angle, flip=True), acc=2, vel=2)
# rob.movej(goto_holder_index(53, 0.1, angle, flip=True), acc=2, vel=2)
#time.sleep(2)
#rob.movej(goto_holder_index(24, 0.15, 35, flip=True), acc=2, vel=2)
#time.sleep(10)
# time.sleep(4)
# goto_holder_index(26, 0.1, 20)
# time.sleep(4)
# goto_holder_index(26, 0.1, 30)
# time.sleep(4)
# goto_holder_index(26, 0.1, 40)
# for joint in config["position_map"]:
#joint = config["position_map"][26]
#print("Going to cable holder index", joint["index"], "at position", joint["pos"])
#angles = get_joints_from_xyz_abs(joint["pos"][1]/1000, joint["pos"][0]/1000, 0.05, )# rx=math.pi / 5)
#joints.append(angles)
#rob.movej(angles, acc=2, vel=2)
#time.sleep(10)
#rob.movejs(joints, acc=2, vel=2)
# joints = []
# for i in np.linspace(-0.3, -0.7, 50):
# joints.append(get_joints_from_xyz_abs(i, 0, 0))
# rob.movejs(joints, acc=2, vel=2)
# time.sleep(5)
# angles = get_joints_from_xyz_abs(0, -0.6, 0)
# rob.movej(angles, acc=2, vel=2)
# set_pos_abs(*p1) robot = Rob(config) # robot of type Rob is the custom class above
# move = move_to_polar(p1, p2) #powerup_arm(robot)
robot = connect(robot)
init_arm(robot)
rob = robot.robot # rob is robot.robot is the urx robot class, what we've been using previously
# for p in move: #move_to_packup(robot)
# print(math.degrees(p)) move_to_home(robot)
# print("Safe? :", is_safe_move(p1, p2)) 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)
# #set_pos_rel_rot_abs(0, 0, -0.2, math.pi, 0, -math.pi) # for i in range(0,54):
# set_pos_abs(0.3, -0.2, 0.5, math.pi, 0, -math.pi) # pick_up_holder(robot, None, i)
# 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)
# print("Current tool pose is: ", rob.getl()) # #print('Drop off', i+1)
# print("getj(): ", rob.getj()) # drop_off_tray(robot, 0)
# #input()
# move_to_home() # # holder_to_camera(robot, 0)
# # camera_to_holder(robot, 0)
print("Current tool pose is: ", rob.getl())
curr_pos = rob.getl()
config = None
rob.stop() rob.stop()
os.kill(os.getpid(), 9) # dirty kill of self os.kill(os.getpid(), 9) # dirty kill of self

View File

@ -26,8 +26,8 @@ if win32:
#if not getattr(sys, "frozen", False): #if not getattr(sys, "frozen", False):
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # hide powershell window startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # hide powershell window
res = subprocess.check_output(["WMIC", "ComputerSystem", "GET", "UserName"], universal_newlines=True, startupinfo=startupinfo) res = subprocess.check_output(["WMIC", "ComputerSystem", "GET", "UserName"], universal_newlines=True, startupinfo=startupinfo)
_, username = res.strip().rsplit("\n", 1) # _, username = res.strip().rsplit("\n", 1)
userid, sysdom = username.rsplit("\\", 1) # userid, sysdom = username.rsplit("\\", 1)
if linux or macos: if linux or macos:
sysid = hex(uuid.getnode()) sysid = hex(uuid.getnode())

View File

@ -81,7 +81,7 @@
socket.send(message); socket.send(message);
console.log('Message sent', message); console.log('Message sent', message);
} }
setInterval(ping, 1500); //setInterval(ping, 1500);
// setInterval(() => { // setInterval(() => {
// updateServiceStatus('serviceA', 'down'); // updateServiceStatus('serviceA', 'down');
@ -150,7 +150,7 @@
setInterval(updateClock, 100); setInterval(updateClock, 100);
</script> </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> </body>
</html> </html>