50 Commits

Author SHA1 Message Date
86971e0db4 Restart VM to clear network leftovers 2024-08-20 09:18:04 -05:00
f04a2a4bd4 remove pick count file 2024-08-20 09:04:56 -05:00
5b7afac517 Update web 2024-08-20 09:04:56 -05:00
ad5eb5bbb3 Update runtime 2024-08-20 09:04:56 -05:00
a822ea5184 Add QR codes for Web UI 2024-08-20 10:05:57 -05:00
5a5202e6f5 Add mode query 2024-08-16 12:12:39 -05:00
3fb78f8b5a Fix simulation mode 2024-08-16 12:09:32 -05:00
48e7c7b85f Update sensor code 2024-08-08 10:51:05 -05:00
ea5daf8689 Switch to unicode encoding 2024-08-06 17:41:45 -05:00
05baceb388 Fix logic 2024-08-06 17:36:31 -05:00
43694ca97d Use alternate method 2024-08-06 17:33:22 -05:00
3f731a4bd3 Use alternate method 2024-08-06 17:30:41 -05:00
419498cae6 Include stderr 2024-08-06 17:26:12 -05:00
c89fc86812 Fix stdout flush 2024-08-06 17:16:20 -05:00
f8507a1bc9 Add more statistics, fix camera, windows client update 2024-08-06 17:01:27 -05:00
534cc23c8d Update MQTT commands, arm height 2024-07-31 18:25:10 -05:00
b677556ece Add counter, MQTT 2024-07-30 12:54:13 -05:00
621a530530 Update web with shutdown procedure 2024-07-29 14:01:20 -05:00
0ad656728d Add shutdown routine 2024-07-29 13:50:58 -05:00
4591dc6900 Add show mode 2024-07-29 13:38:14 -05:00
1ac8712e0f Update web, disable sensors in testing mode 2024-07-29 12:06:16 -05:00
af91817a98 Match LED strip color 2024-07-25 10:43:04 -05:00
24e0094d2a Add system controller camera mode 2024-07-25 10:35:16 -05:00
faf4cc1ba8 Led loop 2024-05-14 21:52:14 -05:00
0d0ec75783 update web 2024-05-14 21:50:46 -05:00
5c4d92a84c Remove temp file 2024-05-14 21:50:11 -05:00
02ab16a566 Add windows client mode updates 2024-05-14 21:51:36 -05:00
927f61e124 update loading app 2024-05-14 20:53:01 -05:00
d69cce2e38 Add camera error message 2024-05-14 20:44:33 -05:00
98fef5ad3d increase camera timeout 2024-05-14 20:42:23 -05:00
fa25b2c8b1 add CORS 2024-05-14 20:29:32 -05:00
32260595a1 Open gripper before picking up 2024-05-14 20:21:09 -05:00
aeeb4c232a slow reset 2024-05-14 20:18:26 -05:00
bbb2b3ff17 speed up 2024-05-14 20:10:47 -05:00
b811351aba remove old var 2024-05-14 20:08:45 -05:00
38ee4f9b9a update sensor system 2024-05-14 20:07:35 -05:00
b56d176c82 test camera 2024-05-14 19:07:49 -05:00
25085f542f Try old method 2024-05-14 18:52:27 -05:00
2562fea076 try this 2024-05-14 18:48:25 -05:00
0ca9f01aa9 use boolean value 2024-05-14 18:44:54 -05:00
8b37661a84 manual open in UR5 control 2024-05-14 18:43:43 -05:00
6347cde9e2 Manually close modbus socket 2024-05-14 18:41:31 -05:00
7885c0900d add camera timeout 2024-05-14 18:32:09 -05:00
c1f783bb2c fix transition to idle after camera fail 2024-05-14 18:23:41 -05:00
a2e7592ef7 adjust 2024-05-14 18:17:58 -05:00
b0d61d7d00 Adjust slot position 2024-05-14 18:14:17 -05:00
9687a6e492 Revert to base urx, reduce camera time 2024-05-14 17:52:53 -05:00
0e6ca7490a Lower frame count 2024-05-14 17:49:10 -05:00
61954b23b9 Slow to 60hz 2024-05-14 17:44:52 -05:00
3ede455d2c Update web 2024-05-14 17:37:18 -05:00
16 changed files with 693 additions and 231 deletions

1
.gitignore vendored
View File

@ -22,3 +22,4 @@ build
# Generated label images
labels
temp
pick_count.txt

29
client.bat Normal file
View File

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

View File

@ -3,7 +3,11 @@ core:
serverip: 172.26.178.114
clientip: 172.26.176.1
server: Hyper-Vd
loopspeed: 100 # fps
loopspeed: 60 # fps
mqtt:
enabled: True
server: 172.31.108.4
arm:
ip: 192.168.1.145
@ -24,10 +28,10 @@ cables:
cameras:
banner:
ip: 192.168.1.125
port: 32200
ip: 192.168.1.199
port: 80
animation_time: 100
animation_time: 60
led:
fps: 100
@ -81,15 +85,15 @@ led:
- universe: 10
ip: 192.168.1.209
mode: solid
color: [0, 50, 150]
color: [0, 50, 100]
- universe: 11
ip: 192.168.1.210
mode: solid
color: [0, 50, 150]
color: [0, 50, 100]
- universe: 12
ip: 192.168.1.211
mode: solid
color: [0, 50, 150]
color: [0, 50, 100]
# - universe: 0
# ip: 192.168.1.209
# ledstart: 1296
@ -264,7 +268,7 @@ led:
size: 24
diameter: 63.5
angle: 180
pos: [197.973, 38.1]
pos: [201.973, 34.1]
- type: circle
start: 624
size: 24
@ -470,7 +474,7 @@ position_map:
- index: 7
pos: [-38.1, 197.973]
- index: 8
pos: [38.1, 197.973]
pos: [34.1, 201.973]
- index: 9
pos: [114.3, 197.973]
- index: 10

View File

@ -2,16 +2,22 @@ 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.
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
handler = http.server.SimpleHTTPRequestHandler
with socketserver.TCPServer(("", port), handler) as httpd:
print(f"Serving cable images & files at port {port}")
# 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

@ -34,6 +34,10 @@ class LEDSystem():
animation_time = 0
start = uptime()
solid_controllers = None
showtoggle = False
clearshow = -1
showring = -1
solidanimcount = 0
def __init__(self):
self.start = uptime()
@ -264,7 +268,41 @@ class LEDSystem():
offset = len(self.controllers)
for x in range(len(self.solid_controllers)):
ctrl = self.solid_controllers[x]
self.sender[ctrl[0]+1].dmx_data = list(ctrl[1]) * 170
data = list((0, 0, 0)) * 170
#print(len(data))
#print(len(list(ctrl[1]) * 170))
offset = 1 - (self.solidanimcount - int(self.solidanimcount))
#print("a", offset)
data[int(self.solidanimcount) * 3] = int(ctrl[1][0]*offset)
data[int(self.solidanimcount) * 3 + 1] = int(ctrl[1][1]*offset)
data[int(self.solidanimcount) * 3 + 2] = int(ctrl[1][2]*offset)
offset = 1 - offset
#print("b", offset)
if int(self.solidanimcount) <= 168:
data[int(self.solidanimcount+1) * 3] = int(ctrl[1][0]*offset)
data[int(self.solidanimcount+1) * 3 + 1] = int(ctrl[1][1]*offset)
data[int(self.solidanimcount+1) * 3 + 2] = int(ctrl[1][2]*offset)
# if offset > 0.5: # onto next light more
# # center +1
# num = int(self.solidanimcount+1)
# else:
# num = int(self.solidanimcount)
# offset = 1 - offset
# if int(num) <= 168:
# data[int(num+1) * 3] = int(ctrl[1][0]*offset/2)
# data[int(num+1) * 3 + 1] = int(ctrl[1][1]*offset/2)
# data[int(num+1) * 3 + 2] = int(ctrl[1][2]*offset/2)
# if int(num) >= 1:
# data[int(num-1) * 3] = int(ctrl[1][0]*offset/2)
# data[int(num-1) * 3 + 1] = int(ctrl[1][1]*offset/2)
# data[int(num-1) * 3 + 2] = int(ctrl[1][2]*offset/2)
self.sender[ctrl[0]+1].dmx_data = data #list(ctrl[1]) * 170
self.solidanimcount += 0.15
if int(self.solidanimcount) >= 170:
self.solidanimcount = 0
self.sender.flush()
@ -333,6 +371,8 @@ class LEDSystem():
def setmode(self, stmode, r=0,g=0,b=0):
if stmode is not None:
if self.mode == "Show":
self.clearshow = self.showring
if self.mode != stmode:
self.firstrun = True
@ -363,6 +403,9 @@ class LEDSystem():
def runmodes(self, ring = -1, arm_position = None):
#fprint("Mode: " + str(self.mode))
if self.clearshow > -1:
self.setring(0,50,100,self.clearshow)
self.clearshow = -1
if self.mode == "Startup":
# loading animation. cable check
if self.firstrun:
@ -452,6 +495,22 @@ class LEDSystem():
self.setring(0,100,0,ring)
self.setmode("Idle")
elif self.mode == "Show":
if self.firstrun:
self.showring = ring
self.firstrun = False
self.showtoggle = False
self.changecount = self.animation_time / 2 # 100hz
if self.changecount > 0:
#self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][3], self.changecount, 0,100,0)
if not self.showtoggle:
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,100,0)
else:
self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 100,20,100)
else:
self.changecount = self.animation_time / 2 # 100hz
self.showtoggle = not self.showtoggle
elif self.mode == "Idle":
if self.firstrun:
self.firstrun = False
@ -749,6 +808,7 @@ if __name__ == "__main__":
import matplotlib.pyplot as plt
ledsys = LEDSystem()
ledsys.init()
while True:
cap = cv2.VideoCapture('output.mp4')
while cap.isOpened():
ret, frame = cap.read()

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

After

Width:  |  Height:  |  Size: 344 KiB

View File

@ -4,38 +4,54 @@ import cv2
import banner_ivu_export
import numpy as np
from util import fprint
import requests
class qr_reader():
camera = None
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.url = "http://" + ip + ":" + str(port) + "/barcode"
#self.camera = banner_ivu_export.DriveImg(ip, port)
# def read_qr(self, tries=1):
# print("Trying " + str(tries) + " frames.")
# self.camera = banner_ivu_export.DriveImg(self.ip, self.port)
# for x in range(tries):
# print(str(x) + " ", end="", flush=True)
# imgtype, img = self.camera.read_img()
# if img is not None:
# #fprint(imgtype)
# image_array = np.frombuffer(img, np.uint8)
# img = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
# #cv2.imshow('Image', img)
# #cv2.waitKey(1)
# detect = cv2.QRCodeDetector()
# value, points, straight_qrcode = detect.detectAndDecode(img)
# if value != "":
# self.camera.close()
# return value
# else:
# print("\nGot no image for " + str(x))
# self.camera.close()
# return False
def read_qr(self, tries=1):
print("Trying " + str(tries) + " frames.")
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
self.camera.close()
try:
response = requests.get(self.url, timeout=tries * 15)
response.raise_for_status() # Raise an error for bad status codes
print(response.text) # Or handle the response as needed
if len(response.text) < 8:
return False
return response.text
except requests.Timeout:
print(f'The request timed out after {tries * 15} seconds')
except requests.RequestException as e:
print(f'An error occurred: {e}')
return False
class video_streamer():
camera = None

View File

@ -17,6 +17,7 @@ import os
import glob
import sys
from PIL import Image
import segno
def touch(path):
with open(path, 'a'):
@ -197,6 +198,18 @@ def parse(filename, output_dir, partnum, dstype, weburl, extra):
else:
img = None
fprint("Making QR code for part " + partnum)
partnumqr = partnum.replace(" ", "%20")
if dstype == "Alphawire":
partnumqr = "AW" + partnumqr
if dstype == "Belden":
partnumqr = "BL" + partnumqr
qrcode = segno.make('HTTPS://BLDN.APP/' + partnumqr,micro=False,boost_error=False,error="L",mask=3)
#out = io.BytesIO()
qrx, _ = qrcode.symbol_size(1,0)
qrcode.save(output_dir + "/qrcode.png", scale=500.0/qrx, kind="PNG", border=0, light="#00000000")
qrpath = weburl + find_file_noext(output_dir, prefix="qrcode")[0]
# Table parsing and reordring
tables = dict()
torename = dict()
@ -368,14 +381,15 @@ def parse(filename, output_dir, partnum, dstype, weburl, extra):
output_table["brand"] = extra["brand"]
else:
output_table["brand"] = dstype
output_table["datasheet"] = weburl + "datasheet.pdf"
output_table["qrcode"] = qrpath
if img is not None:
output_table["image"] = img
output_table["fullspecs"] = {"partnum": partnum, "id": id, "brand": output_table["brand"], "image": img, "datasheet": output_dir + "/datasheet.pdf", **tables}
output_table["searchspecs"] = {"partnum": partnum, "brand": output_table["brand"], "image": img, "datasheet": output_dir + "/datasheet.pdf", **flatten(tables)}
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": output_dir + "/datasheet.pdf", **tables}
output_table["searchspecs"] = {"partnum": partnum, "brand": output_table["brand"], "datasheet": output_dir + "/datasheet.pdf", **flatten(tables)}
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"]

View File

@ -5,7 +5,7 @@ pypdf2==2.12.1
alive-progress
requests
math3d==4.0.0
git+https://git.myitr.org/adeck/python-urx-getl-rt.git
git+https://github.com/Byeongdulee/python-urx.git
meilisearch
pyyaml
Flask
@ -22,6 +22,7 @@ ghostscript
pyzbar
segno
pyModbusTCP
paho-mqtt
# Development
matplotlib

534
run.py
View File

@ -11,6 +11,7 @@ import multiprocessing
from time import sleep
from util import fprint
from util import run_cmd
from util import win32
import sys
import ur5_control
from ur5_control import Rob
@ -30,9 +31,13 @@ from search import JukeboxSearch
from pyModbusTCP.client import ModbusClient
from uptime import uptime
import fileserver
import paho.mqtt.client as mqtt
import pickle
import time
import subprocess
# set to false to run without real hardware for development
real = True
real = False
skip_scanning = True
mbconn = None
@ -73,6 +78,19 @@ arm_position = (0,0,0,0,0,0)
arm_position_process = None
start_animation = False
failcount = 0
timecount = 0
secondsclock = 0
unacked_publish = set()
mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
counter_file = 'pick_count.txt'
cycle_start_time = time.time()
arm_distance = 0
arm_distance_old = 0
arm_distance_total = 0
kill_ssh = False
mqttc.user_data_set(unacked_publish)
spot = -1
placed = 0
def arm_start_callback(res):
fprint("Arm action complete.")
@ -120,6 +138,38 @@ def send_data(type, call, data, client_id="*"):
out["data"] = data
to_server_queue.put((client_id, json.dumps(out)))
def initialize_counter():
global counter
try:
with open(counter_file, 'rb') as file:
counter = int(file.readline().decode())
fprint("Read count at " + str(counter))
if not isinstance(counter, int):
raise ValueError("Counter is not an integer")
except (FileNotFoundError, ValueError) as e:
counter = 101
fprint("Error. Resetting count." + str(e))
fprint(counter)
save_counter()
fprint(counter)
# Save the counter to the file
def save_counter():
global counter
fprint(counter)
with open(counter_file, 'wb') as file:
file.write(str(counter).encode())
# Increment the counter
def increment_counter():
global counter
fprint(counter)
fprint("Setting count to " + str(counter + 1))
counter = counter + 1
save_counter()
def check_server():
#print("HI")
global cable_list
@ -214,6 +264,10 @@ def check_server():
case "ping":
fprint("Pong!!!")
case "mode":
if call == "request":
send_data("mode", "send", "{\"mode\": \"" + mode + "\" }")
# Lucas' notes
# Add a ping pong :) response/handler
# Add a get cable response/handler
@ -331,10 +385,16 @@ def check_server():
mainloop_get.put(("pickup", data["position"]))
elif "tray" in data:
fprint("Adding tray return to dispense queue")
mainloop_get.put(("return", data["tray"]))
mainloop_get.put(("returnCheck", 0))
#mainloop_get.put(("return", data["tray"]))
else:
fprint("Invalid data.")
elif call == 'request':
if "position" in data:
mainloop_get.put(("show", data["position"]))
case "shutdown":
mainloop_get.put(("shutdown", 0))
case _:
fprint("Unknown/unimplemented data type: " + decoded["type"])
except Exception as e:
@ -422,7 +482,8 @@ def setup_server(pool, manager):
if sensor_ready is False:
fprint("waiting for " + "Sensor Initialization" + " to complete...", sendqueue=to_server_queue)
global mbconn
mbconn = ModbusClient(host="192.168.1.20", port=502, auto_open=True, auto_close=False)
if real:
mbconn = ModbusClient(host="192.168.1.20", port=502, auto_open=False, auto_close=False)
get_sensors()
fprint("Sensors initialized.", sendqueue=to_server_queue)
@ -448,20 +509,47 @@ def setup_server(pool, manager):
fprint("Starting websocket server...", sendqueue=to_server_queue)
websocket_process = server.start_websocket_server(to_server_queue, from_server_queue)
fprint("Starting image file server...", sendqueue=to_server_queue)
fprint("Starting file server...", sendqueue=to_server_queue)
image_server_process = Process(target=fileserver.run_server, args=(config["cables"]["port"], config["cables"]["directory"]))
image_server_process.start()
if config["mqtt"]["enabled"]:
global mqttc
global unacked_publish
mqttc.on_publish = on_publish
mqttc.user_data_set(unacked_publish)
mqttc.connect(config["mqtt"]["server"])
mqttc.loop_start()
return True
def mqtt_send(msg, name):
global config
if config["mqtt"]["enabled"]:
global mqttc
global unacked_publish
msg_info = mqttc.publish("jukebox/" + name, msg, qos=1)
unacked_publish.add(msg_info.mid)
while len(unacked_publish):
sleep(0.01)
# Due to race-condition described above, the following way to wait for all publish is safer
msg_info.wait_for_publish()
def handle_error(error):
print(error, flush=True)
def get_sensors():
global mbconn
global sensors
oldsens = sensors
#print("Reading sensors")
# mbconn.open()
if not real:
return -1
if not mbconn.is_open:
mbconn.open()
"""
port 1: 256
port 2: 272
@ -473,41 +561,83 @@ def get_sensors():
port 8: 368
"""
out = list()
if real:
for reg in [352, 288, 304, 368]:
sens = [320, 336, 352, 368]
for idx in range(len(sens)):
reg = sens[idx]
val = mbconn.read_holding_registers(reg)
#fprint("Sensor " + str(idx) + " = " + str(val))
if val is not None:
val = val[0]
if val == 1:
out.append(1)
else:
out.append(0)
else:
out = [0, 0, 0, 0]
if val == 1: # skip negative values
sensors[idx] += 1
elif val == 0:
sensors[idx] -= 1
else:
sensors = [-10, -10, -10, -10]
if len(out) != 4:
return -1
sensors = out
#fprint("Values: " + str(sensors))
mbconn.close()
for x in range(len(oldsens)):
if oldsens[x] == 0 and out[x] == 1:
# cable newly detected on tray
fprint("Precense detected: slot " + str(x))
return x
#mbconn.close()
for x in range(len(sensors)):
if sensors[x] > 10:
sensors[x] = 10
if sensors[x] < -10:
sensors[x] = -10
return -1
def get_open_spot(sensordata):
for x in range(len(sensordata)):
sens = sensordata[x]
if not sens:
for x in range(len(sensors)):
sens = sensors[x]
if sens <= -5:
print("Open spot: " + str(x))
return x
# if we get here, every spot is full
fprint("No spots empty")
return False
def get_full_spot(sensordata):
for x in range(len(sensors)):
sens = sensors[x]
if sens >= 3:
print("Full spot: " + str(x))
return x
# if we get here, every spot is empty
fprint("No spots full")
return False
def get_spot(sensordata, idx):
if sensordata[idx] >= 3:
return True
elif sensordata[idx] <= -5:
return False
else:
return False
def on_publish(client, userdata, mid, reason_code, properties):
# reason_code and properties will only be present in MQTTv5. It's always unset in MQTTv3
try:
userdata.remove(mid)
except KeyError:
print("on_publish() is called with a mid not present in unacked_publish")
print("This is due to an unavoidable race-condition:")
print("* publish() return the mid of the message sent.")
print("* mid from publish() is added to unacked_publish by the main thread")
print("* on_publish() is called by the loop_start thread")
print("While unlikely (because on_publish() will be called after a network round-trip),")
print(" this is a race-condition that COULD happen")
print("")
print("The best solution to avoid race-condition is using the msg_info from publish()")
print("We could also try using a list of acknowledged mid rather than removing from pending list,")
print("but remember that mid could be re-used !")
def mainloop_server(pool, manager):
# NON-blocking loop
@ -531,6 +661,7 @@ def mainloop_server(pool, manager):
global mainloop_get
global cable_list_state
global scan_value
global sensors
global oldmode
global arm_updates
global animation_wait
@ -538,12 +669,23 @@ def mainloop_server(pool, manager):
global arm_position_process
global start_animation
global failcount
global timecount
global secondsclock
global cycle_start_time
global arm_distance
global arm_distance_old
global arm_distance_total
global placed
global spot
if mode != oldmode:
print(" ***** Running mode:", mode, "***** ")
send_data("mode", "send", "{\"mode\": \"" + mode + "\" }")
oldmode = mode
mqtt_send(mode, "mode")
if mode == "Startup": # very first loop
pass
initialize_counter()
if killme.value > 0:
killall()
@ -551,20 +693,28 @@ def mainloop_server(pool, manager):
# check for messages
check_server()
# do every loop!
checkpoint = None
val = None
if not arm_updates.empty():
val = arm_updates.get()
if isinstance(val, tuple):
arm_position = val
if arm_distance_old == 0:
arm_distance_old = val[0] + val[1] + val[2]
arm_distance = 0
else:
arm_distance += val[0] + val[1] + val[2] - arm_distance_old
arm_distance_old = val[0] + val[1] + val[2]
else:
print("Arm queue message " + str(val))
checkpoint = val
print(ring_animation, animation_wait, ledsys.mode, arm_position)
if start_animation:
if start_animation and real:
# animation start requested
# may not be immediate
if ring_animation is not None:
@ -594,12 +744,28 @@ def mainloop_server(pool, manager):
else:
# no new animation
if ring_animation is not None:
if ring_animation is not None and real:
ledsys.mainloop(None, ring_animation, arm_position=arm_position)
else:
pass
elif real:
ledsys.mainloop(None, -1, arm_position=arm_position)
# every 1 second
if secondsclock >= config["core"]["loopspeed"] / 2:
secondsclock = 1
arm_distance_total += abs(arm_distance)
if abs(arm_distance) < 0.001:
arm_distance = 0.0
mqtt_send("{\"value\": " + str(abs(arm_distance)) + " }", "arm_speed")
mqtt_send("{\"value\": " + str(abs(arm_distance_total)) + " }", "arm_distance")
arm_distance_old = 0 # reset counter
get_sensors()
else:
secondsclock += 1
# if start_animation is False and ring_animation is not None and ledsys.mode != "Idle" and real:
# ledsys.mainloop(None, ring_animation, arm_position=arm_position)
@ -654,8 +820,8 @@ def mainloop_server(pool, manager):
global scan_value
if scan_value is False:
cable_list.append(scan_value)
elif scan_value.find("bldn.app/") > -1:
scan_value = scan_value[scan_value.find("bldn.app/")+9:]
elif scan_value.upper().find("BLDN.APP/") > -1:
scan_value = scan_value[scan_value.upper().find("BLDN.APP/")+9:]
else:
cable_list.append(scan_value)
fprint(scan_value)
@ -760,63 +926,24 @@ def mainloop_server(pool, manager):
pass
if mode == "Idle":
# do nothing
if arm_ready is False:
pass
else:
global mainloop_get
#print("Checking sensors..")
newtube = get_sensors()
if newtube >= 0 and newtube != just_placed:
# need to return a cable
mainloop_get.put(("return", newtube))
just_placed = -1
if not mainloop_get.empty():
fprint("Movement requested. Keep clear of the machine!")
action, get_cable = mainloop_get.get()
if get_cable > -1:
global sensors
if action == "pickup":
spot = get_open_spot(sensors)
if spot is not False:
arm_ready = False
if real:
animation_wait = False
ring_animation = get_cable
start_animation = True
led_set_mode = "GrabAA"
pool.apply_async(ur5_control.holder_to_tray, (arm, arm_updates, get_cable, spot), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
fprint("Getting cable at position " + str(get_cable))
mode = "Pickup"
cable_list_state[get_cable] = False # mark as removed
get_sensors()
if action == "return":
arm_ready = False
fprint("Returning cable from tray position " + str(get_cable))
if real:
failcount = 0
pool.apply_async(ur5_control.tray_to_camera, (arm, arm_updates, get_cable), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
mode = "ReturnC"
else:
# LED idle anim
pass
if mode == "Pickup":
# complete
if arm_ready == True:
mode = "Idle"
if not real:
sleep(9)
#global sensors
if placed > 5:
# success
mqtt_send("{\"value\": " + str(1) + " }", "pick_count_success")
mqtt_send("{\"value\": " + str(int(time.time() * 1000) - cycle_start_time) + " }", "cycle_time")
else:
# getting cable and bringing to tray
# led animation
if get_spot(sensors, spot):
placed += 1
if ledsys.mode == "Idle" and led_set_mode != "GrabAA":
animation_wait = True
start_animation = True
@ -834,7 +961,8 @@ def mainloop_server(pool, manager):
start_animation = True
ring_animation = 49
led_set_mode = "Camera"
pool.apply_async(camera.read_qr, (50,), callback=camera_start_callback, error_callback=handle_error)
timecount = 0
pool.apply_async(camera.read_qr, (10,), callback=camera_start_callback, error_callback=handle_error)
else:
camera_ready = True
scan_value = "10GXS13"
@ -848,27 +976,34 @@ def mainloop_server(pool, manager):
if camera_ready == True:
if scan_value is False:
# unable to scan ???? not good
if failcount > 15:
if failcount > 30:
mode = "Idle"
fprint("Giving up scanning cable.")
failcount = 0
timecount = 0
arm_ready = True
else:
fprint("Unable to scan cable. Gonna retry.")
camera_ready = False
#mode = "Idle"
failcount += 1
timecount = 0
pool.apply_async(camera.read_qr, (10,), callback=camera_start_callback, error_callback=handle_error)
elif scan_value.find("bldn.app/") > -1:
scan_value = scan_value[scan_value.find("bldn.app/")+9:]
elif scan_value.upper().find("BLDN.APP/") > -1:
scan_value = scan_value[scan_value.upper().find("BLDN.APP/")+9:].strip("\r\n\t ")
fprint("Got cable: " + str(scan_value))
if scan_value[0:2] == "BL" or scan_value[0:2] == "AW":
scan_value = scan_value[2:]
#print(cable_list)
fprint("Got cable: " + repr(scan_value))
for idx in range(len(cable_list)):
cable = cable_list[idx]
if cable == scan_value and cable_list_state[idx] == False:
if cable is not False and cable.find(scan_value) == 0 and len(scan_value) == len(cable):
if cable_list_state[idx] == False:
cable_list_state[idx] = True # mark cable as returned
arm_ready = False
if real:
@ -876,28 +1011,42 @@ def mainloop_server(pool, manager):
ring_animation = idx
led_set_mode = "GrabC"
start_animation = True
fprint("Returning to spot " + str(idx))
pool.apply_async(ur5_control.camera_to_holder, (arm, arm_updates, idx), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
mode = "Return"
break
elif cable == scan_value and cable_list_state[idx] == True:
fprint("WARNING: Holder still marked as occupied!")
arm_ready = False
else:
print(cable_list)
print("Length:", len(cable_list))
for idx in range(len(cable_list)-1,-1,-1):
cable = cable_list[idx]
if cable is False:
if real:
animation_wait = True
ring_animation = idx
led_set_mode = "GrabC"
start_animation = True
cable_list_state[idx] = True
fprint("Returning to unused spot " + str(idx))
pool.apply_async(ur5_control.camera_to_holder, (arm, arm_updates, idx), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
mode = "Return"
break
if mode == "Scan":
mode = "Idle"
if mode == "Scan":
fprint("Unable to match cable.")
mode = "Idle"
else: # camera not ready
timecount += 1
if timecount > 180:
print("Camera timeout reached.")
timecount = 0
camera_ready = True
arm_ready = True
mode = "Idle"
if mode == "Return":
if arm_ready == True:
@ -910,9 +1059,104 @@ def mainloop_server(pool, manager):
# led animation
pass
if mode == "IdleWait":
if arm_ready is True:
mode = "Idle"
if mode == "Idle":
# do nothing
if arm_ready is False:
mode = "IdleWait"
else:
global mainloop_get
if not mainloop_get.empty():
action, get_cable = mainloop_get.get()
if action == "show":
animation_wait = False
ring_animation = get_cable
start_animation = True
led_set_mode = "Show"
fprint("Showing cable at position " + str(get_cable))
elif action == "shutdown":
fprint("SHUTTING DOWN!!")
pool.apply_async(ur5_control.move_to_packup, (arm,), callback=arm_start_callback, error_callback=handle_error)
sleep(30)
killme.set(1)
elif action == "returnCheck":
print("Checking sensors..")
if real:
newtube = get_full_spot(sensors)
else:
newtube = -1
if newtube is not False and newtube >= 0:
# need to return a cable
mainloop_get.put(("return", newtube))
mainloop_get.put(("returnCheck", 0))
else:
fprint("Movement requested. Keep clear of the machine!")
placed = 0
arm_distance_total = 0
#mqtt_send("{\"value\": " + str(time.time() * 1000) + " }", "cycle_start")
cycle_start_time = int(time.time() * 1000)
increment_counter()
mqtt_send("{\"value\": " + str(1) + " }", "pick_count_total")
if get_cable > -1:
if action == "pickup":
spot = get_open_spot(sensors)
if spot is not False:
arm_ready = False
if real:
animation_wait = False
ring_animation = get_cable
start_animation = True
led_set_mode = "GrabAA"
pool.apply_async(ur5_control.holder_to_tray, (arm, arm_updates, get_cable, spot), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
fprint("Getting cable at position " + str(get_cable))
mode = "Pickup"
cable_list_state[get_cable] = False # mark as removed
if action == "return":
arm_ready = False
fprint("Returning cable from tray position " + str(get_cable))
if real:
failcount = 0
pool.apply_async(ur5_control.tray_to_camera, (arm, arm_updates, get_cable), callback=arm_start_callback, error_callback=handle_error)
else:
arm_ready = True
mode = "ReturnC"
else:
# LED idle anim
pass
def ping(host):
#Returns True if host (str) responds to a ping request.
# Option for the number of packets as a function of
if win32:
param1 = '-n'
param2 = '-w'
param3 = '250'
else:
param1 = '-c'
param2 = '-W'
param3 = '0.25'
# Building the command. Ex: "ping -c 1 google.com"
command = ['ping', param1, '1', param2, param3, host]
return subprocess.call(command, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT) == 0
def run_loading_app():
@ -924,45 +1168,83 @@ def run_loading_app():
app.run(debug=True, use_reloader=False, port=7000)
def setup_client(pool):
# Windows client setup
fprint("Opening browser...")
firefox = webdriver.Firefox()
firefox.fullscreen_window()
global config
global vm_ready
global serverproc
# Open loading wepage
p = Process(target=run_loading_app)
p.start()
firefox.get('http://localhost:7000')
# start Linux server VM
if config["core"]["server"] == "Hyper-V":
run_cmd("Start-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
run_cmd("Restart-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
sleep(2)
fprint("Waiting for VM to start...")
while not ping("192.168.1.25"):
sleep(0.25)
# Wait for VM to start and be reachable over the network
serverproc = Process(target=start_client_socket)
serverproc.start()
fprint("VM online.")
sleep(2)
# Windows client setup
fprint("Running full jukebox control system...")
jb = subprocess.Popen("ssh root@192.168.1.25 -t -- /root/jukebox-software/run.sh".split(' '), stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1, universal_newlines=True, encoding='utf-8')
while True:
if jb.poll() is not None:
break
line = jb.stdout.readline() # Alternatively proc.stdout.read(1024)
if len(line) == 0: # program end
break
print(line, end='')
if line.find("Running mode: Idle") > 0:
# Jukebox started
break # continue with program
pool.apply_async(check_server_online, (config["core"]["serverip"],config["core"]["clientip"]), callback=vm_start_callback)
if jb.poll() is None:
#wait_for(vm_ready, "VM Startup")
fprint("Opening browser...")
#global vm_ready
if vm_ready is False:
fprint("waiting for " + "VM Startup" + " to complete...")
while vm_ready is False:
sleep(0.1)
p.terminate()
firefox.get("http://" + config["core"]["serverip"] + ":8000")
return True
firefox = webdriver.Firefox()
firefox.fullscreen_window()
# firefox.get('http://localhost:7000')
firefox.get('http://192.168.1.25:3000')
global kill_ssh
while True:
if jb.poll() is not None:
break
line = jb.stdout.readline() # Alternatively proc.stdout.read(1024)
if len(line) == 0: # program end
break
print(line, end='')
if kill_ssh is True:
break
firefox.close()
jb.terminate()
run_cmd("Stop-VM -Name Jukebox*")
sleep(2)
killall()
#firefox.execute_script('document.body.style.MozTransform = "scale(0.80)";')
#firefox.execute_script('document.body.style.MozTransformOrigin = "0 0";')
#firefox.execute_script("document.body.style.zoom='80%'")
# import time
# while True:
# time.sleep(2) # Wait for a given interval
# logs = firefox.get_log('browser')
# for entry in logs:
# if "WebSocket connection" in entry['message'] or "ERR_" in entry['message'] or "Failed to connect" in entry['message'] or "failed to connect" in entry['message']:
# print(f"Error detected in console: {entry['message']}")
# firefox.refresh() # Refresh the page on error
# # else:
# # break # Exit the loop or continue depending on your logic
#return True
def mainloop_client(pool):
sleep(0.1)
killall()
# listen for & act on commands from VM, if needed
# mainly just shut down, possibly connect to wifi or something
@ -993,8 +1275,12 @@ def killall():
def killall_signal(a, b):
global config
if config["core"]["server"] == "Hyper-V":
run_cmd("Stop-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
#if config["core"]["server"] == "Hyper-V":
#run_cmd("Stop-VM -Name Jukebox*") # any and all VMs starting with "Jukebox"
if config["core"]["mode"] == "winclient":
global kill_ssh
kill_ssh = True
else:
killall()
def error(msg, *args):

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,2 +0,0 @@
drop_off_tray(robot, 0)

View File

@ -261,11 +261,12 @@ def move_to_home(robot, keep_flip=False, speed=2):
flip(robot)
# Move robot to home position
rob.movej(offset_gripper_angle(robot, *(-0.18, -0.108, 0.35), flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position
rob.movej(offset_gripper_angle(robot, *(-0.18, -0.108, 0.25), flip=is_flipped(robot)), vel=2, acc=2) # Move to safe position
return True
def move_to_packup(robot, speed=0.25):
robot = connect(robot)
rob = robot.robot
# known good starting point to reach store position
@ -580,14 +581,16 @@ def tray_routine(robot, slot=0, pick_up=True):
# Positions for each slot
slot_distance = .052
slot_height = -.015-.0095
first_slot = -0.3084+0.02
slot_height = -.015-.0095+0.007 # add 7mm for shim
first_slot = -0.3084+0.01+0.003 # add 3mm for tray adjust
slot_position = [
[first_slot, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+2*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
[first_slot+3*slot_distance, -0.3426, slot_height, 1.5899, 1.5526, -0.9411],
]
if pick_up:
open_gripper()
rob.movel(slot_position[slot], vel=0.2, acc=1)
# Place/Grab the tube
@ -679,13 +682,48 @@ def camera_to_holder(robot, pos_updates, holder_index, verbose=False):
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)
time.sleep(0.5)
c.write_single_register(112, 0b0)
c.write_single_register(435, 0b10000000)
time.sleep(0.5)
@ -698,14 +736,16 @@ def close_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)
time.sleep(0.5)
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()
@ -738,7 +778,8 @@ def get_position_thread(robot, pos_updates):
# pos_updates.put(tuple(tmpvals))
while start + t > uptime.uptime():
time.sleep(0.0001)
except:
pass
if __name__ == "__main__":
@ -756,9 +797,9 @@ if __name__ == "__main__":
rob = robot.robot # rob is robot.robot is the urx robot class, what we've been using previously
move_to_packup(robot)
# pick_up_holder(robot, 2)
#move_to_packup(robot)
move_to_home(robot)
pick_up_holder(robot, None, 8)
#drop_off_tray(robot, 0)
# drop_off_tray(robot, 1)
# drop_off_tray(robot, 2)

View File

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