diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5bb7116 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM python:latest + +RUN apt-get update && apt-get install -y libgl1-mesa-glx ghostscript && apt-get clean && rm -rf /var/lib/apt/lists +COPY . . +#COPY config-server.yml config.yml +RUN pip3 install -r requirements.txt + +CMD ["python3", "run.py"] +EXPOSE 5000 +EXPOSE 8000 +EXPOSE 9000 diff --git a/banner_ivu_export.py b/banner_ivu_export.py new file mode 100755 index 0000000..cb61bb5 --- /dev/null +++ b/banner_ivu_export.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 + +import socket +from datetime import datetime +from time import sleep +from util import fprint + +""" + +from: https://github.com/MoisesBrito31/ve_data_log/blob/main/serverContagem/VE/drive.py +(no license) + +(partially) adapted to English language & iVu camera instead of classic VE by Cole Deck + +""" + +def gravaLog(ip="1",tipo="Evento", msg="", file="log_imagem.txt"): + # removed full logging + fprint(msg) + +class DriveImg(): + HEADERSIZE = 100 + ip = "192.168.0.1" + port = 32200 + onLine = False + + def __init__(self, ip, port, pasta = "media/"): + self.pasta = pasta + self.ip=ip + self.port = port + self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + self.trans.settimeout(5) + fprint("Trying to connect...") + try: + self.trans.connect((self.ip,self.port)) + self.onLine = True + fprint("Camera Online") + #self.trans.close() + except: + self.onLine = False + fprint("Offline") + + def read_img(self): + resposta = 'Falha' + try: + if not self.onLine: + #print(f'tentando Conectar camera {self.ip}...') + gravaLog(ip=self.ip,msg=f'Trying to connect...') + sleep(2) + try: + self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + self.trans.connect((self.ip,self.PORT)) + self.onLine = True + gravaLog(ip=self.ip,msg=f'Connection established.') + except: + self.onLine = False + self.trans.close() + return resposta + ret = self.trans.recv(64) + try: + valida = str(ret[0:15].decode('UTF-8')) + #print(valida) + if valida.find("TC IMAGE")<0: + self.onLine = False + self.trans.close() + sleep(2) + gravaLog(ip=self.ip,tipo="Falha",msg=f'Unable to find TC IMAGE bookmark') + return "Error" + except Exception as ex: + self.onLine = False + self.trans.close() + sleep(2) + gravaLog(ip=self.ip,tipo="Falha",msg=f'Error - {str(ex)}') + return "Error" + if ret: + frame = int.from_bytes(ret[24:27],"little") + isJpeg = int.from_bytes(ret[32:33],"little") + img_size = int.from_bytes(ret[20:23],"little") + data = self.trans.recv(5000) + while img_size>len(data) and ret: + ret = self.trans.recv(10000) + if ret: + data = data+ret + #print(f'{len(ret)}b dados recebidos, total de: {len(data)+64}b') + else: + gravaLog(ip=self.ip,tipo="Falha",msg="Unable to recieve the image") + self.onLine = False + return "Unable to recieve the image" + hoje = datetime.now() + idcam = self.ip.split('.') + """try: + nomeFile = f'{hoje.day}{hoje.month}{hoje.year}-{idcam[3]}-{frame}' + if isJpeg==1: + file = open(f'{self.pasta}{nomeFile}.jpg','wb') + nomeFile = f'{nomeFile}.jpg' + else: + file = open(f'{self.pasta}{nomeFile}.bmp','wb') + nomeFile = f'{nomeFile}.bmp' + file.write(data) + file.close() + except Exception as ex: + sleep(2) + gravaLog(ip=self.ip,tipo="Falha",msg=f'Error - {str(ex)}') + return "Falha" + """ + if isJpeg==1: + return "jpeg",data + else: + return "bmp",data + + except Exception as ex: + gravaLog(ip=self.ip,tipo="Falha Generica",msg=f'Error - {str(ex)}') + #print(f'erro {str(ex)}') + self.onLine = False + self.trans.close() + sleep(2) + return resposta + +class DriveData(): + HEADERSIZE = 100 + ip = "192.168.0.1" + port = 32100 + onLine = False + + def __init__(self, ip, port): + gravaLog(ip=self.ip,msg=f'iniciou drive',file="log_data.txt") + self.ip=ip + self.port = port + self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + try: + self.trans.connect((self.ip,self.port)) + self.onLine = True + except: + self.onLine = False + + def read_data(self): + resposta = 'falha' + try: + if not self.onLine: + #print(f'tentando Conectar...\n') + gravaLog(ip=self.ip,msg=f'tentando Conectar...',file="log_data.txt") + sleep(2) + try: + self.trans = socket.socket(socket.AF_INET,socket.SOCK_STREAM) + self.trans.connect((self.ip,self.PORT)) + self.onLine = True + gravaLog(ip=self.ip,msg=f'Conexão restabelecida...',file="log_data.txt") + except: + self.onLine = False + return resposta + resposta = self.trans.recv(self.HEADERSIZE).decode("utf-8") + resposta = str(resposta).split(',') + return resposta + except Exception as ex: + self.onLine = False + gravaLog(ip=self.ip,tipo="Falha Generica",msg=f'erro {str(ex)}',file="log_data.txt") + sleep(2) + return resposta + + +if __name__ == "__main__": + test = DriveImg("192.168.1.125", 32200) + x = 0 + while x < 100: + x=x+1 + imgtype, img = test.read_img() \ No newline at end of file diff --git a/config.yml b/config.yml index 6c86f16..6356135 100644 --- a/config.yml +++ b/config.yml @@ -7,6 +7,12 @@ core: arm: ip: 192.168.1.145 +#cable_map: +cameras: + banner: + ip: 192.168.1.125 + port: 32200 + led: fps: 90 timeout: 0 @@ -26,13 +32,13 @@ led: ledstart: 288 ledend: 431 mode: rgb - - universe: 1 - ip: 192.168.68.130 + - universe: 4 + ip: 192.168.5.40 ledstart: 432 ledend: 575 mode: rgb - - universe: 4 - ip: 192.168.68.131 + - universe: 1 + ip: 192.168.5.4 ledstart: 576 ledend: 719 mode: rgb diff --git a/database.py b/database.py new file mode 100644 index 0000000..2befec2 --- /dev/null +++ b/database.py @@ -0,0 +1,140 @@ +"""This module contains functionality for interacting with a PostgreSQL database. It will automatically handle error +conditions (i.e. missing columns) without terminating the entire program. Use the :py:class:`DBConnector` class to +handle database interactions, either as a standalone object or in a context manager.""" +from __future__ import annotations + +import os +import psycopg2 +from psycopg2 import DatabaseError, OperationalError +from psycopg2.errors import UndefinedColumn + +DB_ADDRESS = os.getenv('DB_ADDRESS', 'localhost') +DB_PORT = os.getenv('DB_PORT', 5432) +DB_USER = os.getenv('DB_USER', 'postgres') +DB_PASSWORD = os.getenv('DB_PASSWORD', '') +DB_NAME = os.getenv('DB_NAME', 'postgres') +DB_TABLE = os.getenv('DB_TABLE', 'cables') + + +class DBConnector: + """Context managed database class. Use with statements to automatically open and close the database connection, like + so: + + .. code-block:: python + with DBConnector() as db: + db.read() + """ + + def _db_start(self): + """Setup the database connection and cursor.""" + try: + self.conn = psycopg2.connect( + f"host={DB_ADDRESS} port={DB_PORT} dbname={DB_NAME} user={DB_USER} password={DB_PASSWORD}") + self.cur = self.conn.cursor() + except OperationalError as e: + raise e + + def _db_stop(self): + """Close the cursor and connection.""" + self.cur.close() + self.conn.close() + + def __init__(self): + self._db_start() + + def __del__(self): + self._db_stop() + + def __enter__(self): + self._db_start() + + def __exit__(self): + self._db_stop() + + def _get_cols(self) -> set[str]: + """Get the list of columns in the database. + + :return: A list of column names.""" + query = f"select COLUMN_NAME from information_schema.columns where table_name={DB_TABLE}" + rows = {x["COLUMN_NAME"] for x in self._query(query)} + return rows + + def _column_parity(self, columns: list[str] | set[str]) -> set[str]: + """If the listed columns are not in the database, add them. + + :param columns: The columns we expect are in the database. + :return: The list of columns in the database after querying.""" + cols = set(columns) + existing = self._get_cols() + needs = cols.difference(existing.intersection(cols)) + if len(needs) > 0: + query = f"ALTER TABLE {DB_TABLE} {', '.join([f'ADD COLUMN {c}' for c in needs])}" + self._query(query) + existing = self._get_cols() + return existing + + def _query(self, sql) -> list[dict]: + """Basic function for running queries. + + :param sql: SQL query as plaintext. + :return: Results of the query, or an empty list if none.""" + result = [] + try: + self.cur.execute(sql) + result = self._read_dict() + except DatabaseError as e: + print(f"ERROR {e.pgcode}: {e.pgerror}\n" + f"Caused by query: {sql}") + finally: + return result + + def _read_dict(self) -> list[dict]: + """Read the cursor as a list of dictionaries. psycopg2 defaults to using a list of tuples, so we want to convert + each row into a dictionary before we return it.""" + cols = [i.name for i in self.cur.description] + results = [] + for row in self.cur: + row_dict = {} + for i in range(0, len(row)): + if row[i]: + row_dict = {**row_dict, cols[i]: row[i]} + results.append(row_dict) + return results + + def read(self, **kwargs) -> list[dict]: + """Read rows from a database that match the specified filters. + + :param kwargs: Column constraints; i.e. what value to filter by in what column. + :returns: A list of dictionaries of all matching rows, or an empty list if no match.""" + args = [] + for kw in kwargs.keys(): + args.append(f"{kw} ILIKE {kwargs['kw']}") + query = f"SELECT * FROM {DB_TABLE}" + if len(args) > 0: + query += f" WHERE {' AND '.join(args)}" + return self._query(query) + + def write(self, **kwargs) -> dict: + """Write a row to the database. + + :param kwargs: Values to write for each database; specify each column separately! + :returns: The row you just added.""" + self._column_parity(set(kwargs.keys())) + values = [] + for val in kwargs.keys(): + values.append(kwargs[val]) + query = f"INSERT INTO {DB_TABLE} ({', '.join(kwargs.keys())}) VALUES ({', '.join(values)})" + self._query(query) + return kwargs + + def write_all(self, items: list[dict]) -> list[dict]: + """Write multiple rows to the database. + + :param items: Rows to write, as a list of dictionaries. + :returns: The rows that were added successfully.""" + successes = [] + for i in items: + res0 = self.write(**i) + if res0: + successes.append(res0) + return successes diff --git a/get_specs.py b/get_specs.py index 419421b..7a17f04 100755 --- a/get_specs.py +++ b/get_specs.py @@ -26,7 +26,7 @@ def check_internet(url='https://belden.com', timeout=5): -def query_search(partnum): +def query_search(partnum, source): """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) @@ -49,15 +49,50 @@ def query_search(partnum): # Bash script uses some crazy json formatting that I could not figure out # Despite the fact that I wrote it # So I'll just leave it, becuase it works. + if source == "Belden": + command = ["./query-search.sh", partnum] + result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if result.returncode != 0: # error + fprint("No results found in search database for " + partnum + ". No hi-res part image available.", result.stderr) + return False + else: + data_out = json.loads(result.stdout) + return data_out + elif source == "Alphawire": + alphaurl = "https://www.alphawire.com//sxa/search/results/?l=en&s={4A774076-6068-460C-9CC6-A2D8E85E407F}&itemid={BF82F58C-EFD9-4D8B-AE3E-097DD12CF7DA}&sig=&autoFireSearch=true&productpartnumber=*" + partnum + "*&v={B22CD56D-AB95-4048-8AA1-5BBDF2F2D17F}&p=10&e=0&o=ProductPartNumber%2CAscending" + r = requests.get(url=alphaurl) + data = r.json() + output = dict() + #print(data) + try: + if data["Count"] > 0: + print(data["Results"][0]["Url"]) + result = data["Results"][0] + if result["Url"].split("/")[-1] == partnum: + #print(partnum) + print(result["Html"]) + try: + imgidx = result["Html"].index("<img src=") + 10 + imgidx2 = result["Html"].index("?", imgidx) + output["image"] = result["Html"][imgidx:imgidx2] + if output["image"].index("http") != 0: + output["image"] = "" + print("No cable image found.") + except: + print("No cable image found.") - command = ["./query-search.sh", partnum] - result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) - if result.returncode != 0: # error - fprint("No results found in search database for " + partnum + ". No hi-res part image available.", result.stderr) + dsidx = result["Html"].index("<a href=\"/disteAPI/") + 9 + dsidx2 = result["Html"].index(partnum, dsidx) + len(partnum) + output["datasheet"] = "https://www.alphawire.com" + result["Html"][dsidx:dsidx2] + #"test".index() + print(output) + return output + + + except: + return False return False - else: - data_out = json.loads(result.stdout) - return data_out + def touch(path): with open(path, 'a'): @@ -126,7 +161,7 @@ def get_multi(partnums): sys.exit() - def _download_image(url, output_dir): # Download datasheet with known URL + def _download_image(url, output_dir): # Download image with known URL global bartext #fprint(url) @@ -151,25 +186,31 @@ def get_multi(partnums): os.remove(partnum + "/datasheet.pdf") sys.exit() - def __use_cached_datasheet(partnum, path, output_dir): + def __use_cached_datasheet(partnum, path, output_dir, dstype): fprint("Using cached datasheet for " + partnum) bar.text = "Using cached datasheet for " + partnum bar(skipped=True) fprint("Parsing Datasheet contents of " + partnum) bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..." - read_datasheet.parse(path, output_dir) + read_datasheet.parse(path, output_dir, partnum, dstype) bar(skipped=False) - def __downloaded_datasheet(partnum, path, output_dir): + def __downloaded_datasheet(partnum, path, output_dir, dstype): fprint("Downloaded " + path) bar.text = "Downloaded " + path bar(skipped=False) fprint("Parsing Datasheet contents of " + partnum) bar.text = "Parsing Datasheet contents of " + partnum + ".pdf..." - read_datasheet.parse(path, output_dir) + read_datasheet.parse(path, output_dir, partnum, dstype) bar(skipped=False) - for partnum in partnums: + for fullpartnum in partnums: + if fullpartnum[0:2] == "BL": # catalog.belden.com entry\ + partnum = fullpartnum[2:] + dstype = "Belden" + elif fullpartnum[0:2] == "AW": + partnum = fullpartnum[2:] + dstype = "Alphawire" output_dir = "cables/" + partnum path = output_dir + "/datasheet.pdf" bartext = "Downloading files for part " + partnum @@ -177,7 +218,7 @@ def get_multi(partnums): # if (not os.path.exists(output_dir + "/found_part_hires")) or not (os.path.exists(path) and os.path.getsize(path) > 1): # Use query - search_result = query_search(partnum.replace(" ", "")) + search_result = query_search(partnum.replace(" ", ""), dstype) # Try to use belden.com search if search_result is not False: # Download high resolution part image if available and needed @@ -190,17 +231,17 @@ def get_multi(partnums): # Download datasheet from provided URL if needed if os.path.exists(path) and os.path.getsize(path) > 1: - __use_cached_datasheet(partnum, path, output_dir) + __use_cached_datasheet(partnum, path, output_dir, dstype) elif _download_datasheet(search_result["datasheet"], output_dir) is not False: - __downloaded_datasheet(partnum, path, output_dir) + __downloaded_datasheet(partnum, path, output_dir, dstype) elif os.path.exists(path) and os.path.getsize(path) > 1: - __use_cached_datasheet(partnum, path, output_dir) + __use_cached_datasheet(partnum, path, output_dir, dstype) # 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) is not False: - __downloaded_datasheet(partnum, path, output_dir) + __downloaded_datasheet(partnum, path, output_dir, dstype) # Failed to download with search or guess :( else: @@ -213,7 +254,7 @@ def get_multi(partnums): # We already have a hi-res image and the datasheet - perfect! else: fprint("Using cached hi-res part image for " + partnum) - __use_cached_datasheet(partnum, path, output_dir) + __use_cached_datasheet(partnum, path, output_dir, dstype) if len(failed) > 0: fprint("Failed to download:") @@ -227,21 +268,22 @@ def get_multi(partnums): if __name__ == "__main__": - partnums = ["10GXS12", "RST 5L-RKT 5L-949", -"10GXS13", -"10GXW12", -"10GXW13", -"2412", -"2413", -"OSP6AU", -"FI4D024P9", -"FISD012R9", -"FDSD012A9", -"FSSL024NG", -"FISX006W0", -"FISX00103", -"C6D1100007" + partnums = ["BL7958A", "BL10GXS12", "BLRST 5L-RKT 5L-949", +"BL10GXS13", +"BL10GXW12", +"BL10GXW13", +"BL2412", +"BL2413", +"BLOSP6AU", +"BLFI4D024P9", +"BLFISD012R9", +"BLFDSD012A9", +"BLFSSL024NG", +"BLFISX006W0", +"BLFISX00103", +"BLC6D1100007" ] get_multi(partnums) + #query_search("3248", "Alphawire") diff --git a/led_control.py b/led_control.py index 4a55fff..75c7823 100755 --- a/led_control.py +++ b/led_control.py @@ -170,7 +170,7 @@ def init(): data = list() for x in range(len(leds)): if leds_size[x] == 3: - data.append((50,50,255)) + data.append((20,20,127)) elif leds_size[x] == 4: data.append((50,50,255,0)) else: @@ -178,9 +178,9 @@ def init(): sendall(data) #time.sleep(50000) fprint("Running start-up test sequence...") - for y in range(1): + for y in range(100): for x in range(len(leds)): - setpixel(5,5,5,x) + setpixel(0,0,150,x) sendall(data) #time.sleep(2) #alloffsmooth() @@ -290,7 +290,7 @@ def close(): time.sleep(0.5) sender.stop() -def mapimage(image, fps=30): +def mapimage(image, fps=90): global start while uptime() - start < 1/fps: time.sleep(0.00001) diff --git a/map.png b/map.png index 6f73f78..52f3cba 100644 Binary files a/map.png and b/map.png differ diff --git a/process_video.py b/process_video.py new file mode 100755 index 0000000..42b020a --- /dev/null +++ b/process_video.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import cv2 +import banner_ivu_export +import numpy as np +from util import fprint + +class qr_reader(): + camera = None + def __init__(self, ip, port): + self.camera = banner_ivu_export.DriveImg(ip, port) + + def read_qr(self, tries=1): + print("Trying " + str(tries) + " frames.") + for x in range(tries): + try: + imgtype, img = self.camera.read_img() + #fprint(imgtype) + image_array = np.frombuffer(img, np.uint8) + img = cv2.imdecode(image_array, cv2.IMREAD_COLOR) + #cv2.imshow('Image', img) + #cv2.waitKey(1) + detect = cv2.QRCodeDetector() + value, points, straight_qrcode = detect.detectAndDecode(img) + return value + except: + continue + return False + + +class video_streamer(): + camera = None + def __init__(self, ip, port): + self.camera = banner_ivu_export.DriveImg(ip, port) + + def get_frame(self): + try: + return self.camera.read_img() + except: + return False + +if __name__ == "__main__": + test = qr_reader("192.168.1.125", 32200) + while True: + fprint(test.read_qr(5)) \ No newline at end of file diff --git a/read_datasheet.py b/read_datasheet.py index a919f8d..ef54a7c 100755 --- a/read_datasheet.py +++ b/read_datasheet.py @@ -9,8 +9,10 @@ from PIL import Image import io import json from util import fprint +import uuid +from util import run_cmd -def parse(filename, output_dir): +def parse(filename, output_dir, partnum, dstype): # Extract table data @@ -22,6 +24,7 @@ def parse(filename, output_dir): page = reader.pages[0] table_list = {} for table in tables: + table.df.infer_objects(copy=False) table.df.replace('', np.nan, inplace=True) table.df.dropna(inplace=True, how="all") table.df.dropna(inplace=True, axis="columns", how="all") @@ -137,44 +140,104 @@ def parse(filename, output_dir): # multi-page table check - if table_name.isdigit() and len(tables) > 1: - fprint(table_name) - fprint(previous_table) - - - - - main_key = previous_table - cont_key = table_name - fprint(tables) - if vertical == False: - main_keys = list(tables[main_key].keys()) - for i, (cont_key, cont_values) in enumerate(tables[cont_key].items()): - if i < len(main_keys): - fprint(tables[main_key][main_keys[i]]) - tables[main_key][main_keys[i]] = (tables[main_key][main_keys[i]] + (cont_key,) + cont_values) - - del tables[table_name] - - else: - for key in tables[cont_key].keys(): - tables[main_key][key] = tables[cont_key][key] - del tables[table_name] + if dstype == "Belden": + if table_name.isdigit() and len(tables) > 1: + fprint(table_name) + fprint(previous_table) + + + + + main_key = previous_table + cont_key = table_name + fprint(tables) + if vertical == False: + main_keys = list(tables[main_key].keys()) + for i, (cont_key, cont_values) in enumerate(tables[cont_key].items()): + if i < len(main_keys): + fprint(tables[main_key][main_keys[i]]) + tables[main_key][main_keys[i]] = (tables[main_key][main_keys[i]] + (cont_key,) + cont_values) + + del tables[table_name] + + else: + for key in tables[cont_key].keys(): + tables[main_key][key] = tables[cont_key][key] + del tables[table_name] previous_table = table_name + # remove multi-line values that occasionally squeak through + def replace_newlines_in_dict(d): + for key, value in d.items(): + if isinstance(value, str): + # Replace \n with " " if the value is a string + d[key] = value.replace('\n', ' ') + elif isinstance(value, dict): + # Recursively call the function if the value is another dictionary + replace_newlines_in_dict(value) + return d + + tables = replace_newlines_in_dict(tables) - fprint(tables) - with open(output_dir + "/tables.json", 'w') as json_file: - json.dump(tables, json_file) + # summary + + output_table = dict() + output_table["partnum"] = partnum + id = str(uuid.uuid4()) + output_table["id"] = id + #output_table["position"] = id + #output_table["brand"] = brand + output_table["fullspecs"] = tables + output_table["searchspecs"] = {"partnum": partnum, **flatten(tables)} + + output_table["searchspecs"]["id"] = id + + print(output_table) + + run_cmd("rm " + output_dir + "/*.json") # not reliable! + with open(output_dir + "/" + output_table["searchspecs"]["id"] + ".json", 'w') as json_file: + json.dump(output_table["searchspecs"], json_file) + + return output_table +def flatten(tables): + def convert_to_number(s): + try: + # First, try converting to an integer. + return int(s) + except ValueError: + # If that fails, try converting to a float. + try: + return float(s) + except ValueError: + # If it fails again, return the original string. + return s + out = dict() + print("{") + for table in tables.keys(): + for key in tables[table].keys(): + if len(key) < 64: + keyname = key + else: + keyname = key[0:64] - return tables + fullkeyname = (table + ": " + keyname).replace(".","") + if type(tables[table][key]) is not tuple: + out[fullkeyname] = convert_to_number(tables[table][key]) + print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",") + elif len(tables[table][key]) == 1: + out[fullkeyname] = convert_to_number(tables[table][key][0]) + + print("\"" + keyname + "\":", "\"" + str(out[fullkeyname]) + "\",") + + print("}") + return out if __name__ == "__main__": - parse("test2.pdf", "10GXS13") \ No newline at end of file + parse("test2.pdf", "cables/10GXS13", "10GXS13") \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index f53fce0..68b28c7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ pypdf2==2.12.1 alive-progress requests git+https://github.com/Byeongdulee/python-urx.git +psycopg2-binary pyyaml Flask selenium diff --git a/run.py b/run.py index 287e4fc..e2bedef 100755 --- a/run.py +++ b/run.py @@ -21,6 +21,7 @@ import led_control import server import asyncio import json +import process_video @@ -34,6 +35,7 @@ vm_ready = False killme = None #pool = None serverproc = None +camera = None to_server_queue = Queue() from_server_queue = Queue() @@ -78,7 +80,7 @@ def start_server_socket(): app.run(host='0.0.0.0', port=5000)""" global to_server_queue global from_server_queue - + fprint("Starting WebSocket server...") websocket_process = server.start_websocket_server(to_server_queue, from_server_queue) # Example @@ -86,9 +88,10 @@ def start_server_socket(): while True: + #print("HI") if not from_server_queue.empty(): - message = from_server_queue.get() - fprint(f"Message from client: {message}") + client_id, message = from_server_queue.get() + fprint(f"Message from client {client_id}: {message}") # Message handler try: @@ -141,6 +144,13 @@ def start_server_socket(): fprint("") elif call == "request": fprint("") + if data["enabled"] == True: + # todo : send this to client + p = Process(target=run_cmd, args=("./keyboard-up.ps1",)) + p.start() + elif data["enabled"] == False: + p = Process(target=run_cmd, args=("./keyboard-down.ps1",)) + p.start() case "machine_settings": fprint("machine_settings message") @@ -202,10 +212,10 @@ def setup_server(pool): global led_ready global arm_ready global serverproc + global camera pool.apply_async(ur5_control.init, (config["arm"]["ip"],), callback=arm_start_callback) pool.apply_async(led_control.init, callback=led_start_callback) - #pool.apply_async(camera_control.init, callback=camera_start_callback) #pool.apply_async(sensor_control.init, callback=sensor_start_callback) serverproc = Process(target=start_server_socket) serverproc.start() @@ -225,10 +235,11 @@ def setup_server(pool): if camera_ready is False: fprint("waiting for " + "Camera initilization" + " to complete...", sendqueue=to_server_queue) - while camera_ready is False: - sleep(0.1) + camera = process_video.qr_reader(config["cameras"]["banner"]["ip"], config["cameras"]["banner"]["port"]) + fprint("Camera initialized.", sendqueue=to_server_queue) + arm_ready = True if arm_ready is False: fprint("waiting for " + "UR5 initilization" + " to complete...", sendqueue=to_server_queue) while arm_ready is False: @@ -251,6 +262,9 @@ def mainloop_server(pool): killall() counter = counter + 1 + fprint("Looking for QR code...") + print(camera.read_qr(30)) + def run_loading_app(): app = Flask(__name__) diff --git a/server.py b/server.py index 63ff41c..17dab98 100755 --- a/server.py +++ b/server.py @@ -50,32 +50,37 @@ def run_server(): import asyncio import websockets from multiprocessing import Process, Queue +from util import fprint +import uuid -connected_clients = set() +connected_clients = {} async def handler(websocket, path, to_server_queue, from_server_queue): # Register websocket connection - connected_clients.add(websocket) + client_id = str(uuid.uuid4()) + connected_clients[client_id] = websocket try: # Handle incoming messages async for message in websocket: #print(f"Received message: {message}") - from_server_queue.put(message) # Put received message into from_server_queue + print(client_id) + from_server_queue.put((client_id, message)) finally: # Unregister websocket connection - connected_clients.remove(websocket) + if client_id in connected_clients: + del connected_clients[client_id] + print(f"Client {client_id} connection closed") async def send_messages(to_server_queue): while True: if not to_server_queue.empty(): - message = to_server_queue.get() - if connected_clients: # Check if there are any connected clients - #await asyncio.wait([client.send(message) for client in connected_clients]) - #await [client.send(message) for client in connected_clients] - for client in connected_clients: + client_id, message = to_server_queue.get() + if client_id in connected_clients: # Send message to specific client + await connected_clients[client_id].send(message) + elif len(connected_clients) > 0: # Broadcast message to all clients + for client in connected_clients.values(): await client.send(message) - - await asyncio.sleep(0.1) # Prevent the loop from running too fast + await asyncio.sleep(0.001) def websocket_server(to_server_queue, from_server_queue): start_server = websockets.serve(lambda ws, path: handler(ws, path, to_server_queue, from_server_queue), "localhost", 9000) diff --git a/util.py b/util.py index 77b9c91..b957305 100755 --- a/util.py +++ b/util.py @@ -62,7 +62,7 @@ def fprint(msg, settings = None, sendqueue = None): print(logMsg) if (sendqueue is not None): - sendqueue.put("{ \"type\": \"log\", \"call\":\"send\", \"data\": \"" + logMsg + "\" }") + sendqueue.put(("*", "{ \"type\": \"log\", \"call\":\"send\", \"data\": \"" + logMsg + "\" }")) if (settings is not None): tmpList = settings["logMsg"] tmpList.append(logMsg) @@ -111,9 +111,9 @@ def run_cmd(cmd): return completed def setup_child(sets=None): - #if not getattr(sys, "frozen", False): - # sys.stdout = Logger(filename=find_data_file("output.log")) - # sys.stderr = Logger(filename=find_data_file("output.log")) + if not getattr(sys, "frozen", False): + sys.stdout = Logger(filename=find_data_file("output.log")) + sys.stderr = Logger(filename=find_data_file("output.log")) if sets is not None: settings = sets @@ -123,7 +123,7 @@ class Logger(object): self.terminal = sys.stdout def write(self, message): - self.log.write(message) + #self.log.write(message) #close(filename) #self.log = open(filename, "a") try: