#!/bin/env python # 必须写在第一行
# -*- coding: utf-8 -*-
#################################################
# LOAD_MODE__
import os
import re
import sys
from PyQt5.QtCore import Qt, QRegExp, QEvent, QThread, QTimer, QObject, pyqtSignal
from PyQt5.QtWidgets import QWidget, QApplication, QMessageBox, QDialog
# from ui import param
from PyQt5.QtGui import QRegExpValidator
from messageBox import messageBox
from collections import defaultdict
from py39COM import Gateway, InCAM
from py39Tools import TableWidget
from ICO import ICO
from ICNET import ICNET
from EqHelper import EqHelper
# from fastmcp import Api, Resource
# import tkinter as tk
import requests
import threading
from Ui_interface import Ui_Dialog as inerfaceui
from Ui_job_input import Ui_ChildWindow as job_input_ui
from Ui_get_layer import Ui_ChildWindow2 as get_layer_ui
from Ui_draw_circle import Ui_ChildWindow3 as draw_circle_ui
import subprocess
import psutil
import time
import netifaces
import json
from pypinyin import lazy_pinyin
from fastapi import FastAPI
import uvicorn
# from img import apprcc_rc
# from pprint import pprint
# from sublib import
# print('apprcc_rc.rcc_version : %s' % apprcc_rc.rcc_version)
#第一步:在终端运行python interface.py
#第二步:点击按钮确认GUI功能
#第三步:保持 interface.py 运行状态,在另一个终端中测试:curl http://127.0.0.1:8000,会返回:{"message": "Hello, World!"}类似信息
##测试调用curl -X POST http://127.0.0.1:8000/api/open_incam,返回json信息。对其他接口同样进行测试,如:curl -X POST http://127.0.0.1:8000/api/open_job
# #第四步:运行该代码,在浏览器输入:http://127.0.0.1:8000/docs
# MODE_END__
def get_pinyin(chinese):
if not chinese:
return ''
str = chinese
new_str = str[0:]
# print(new_str)
new_str1 =''.join(lazy_pinyin(new_str))
# print(new_str1)
return new_str1
def GetUserInfo(mode=None):
'''
从指定URL获取主机信息(JSON格式)
返回值:成功返回JSON对象,失败返回None
'''
getHtml = 'http://sheet.scc.com/gethosts/format/json'
resp = requests.get(getHtml, timeout=60)
if resp.status_code == 200:
json_obj = json.loads(resp.content.decode())
return json_obj
else:
return None
def get_local_ip():
"""
获取本机 IP 地址,优先返回 10.x.x.x,其次 192.168.x.x
"""
ip_list = []
for interface in netifaces.interfaces():
addrs = netifaces.ifaddresses(interface)
if netifaces.AF_INET in addrs:
for addr_info in addrs[netifaces.AF_INET]:
ip = addr_info['addr']
if ip.startswith('10.'):
return ip # 立即返回第一个10.x地址
ip_list.append(ip)
#找到的是192开头的ip地址则转换成10开头的ip
if ip.startswith('192.168.200.'):
parts = ip.split('.')
new_ip = f'10.61.26.{parts[3]}'
return new_ip
ip_list.append(ip)
# 如果没有10.x地址,返回第一个192.x地址
for ip in ip_list:
if ip.startswith('192.'):
return ip
# 都没有则返回第一个找到的IP
return ip_list[0] if ip_list else "127.0.0.1"
def get_username():
try:
local_ip = get_local_ip()
# print(f"Local IP address: {local_ip}")
user_info = GetUserInfo(local_ip)
if not user_info or not isinstance(user_info, list):
print("用户信息为空或格式错误。")
return None
res= [x for x in user_info if x['ip_address'] == local_ip]
if len(res) == 0:
return None
username = res[0]['user']
# print(f"准备调用 SubCenterGetUserInfo,传入 username: {username}")
englishname = get_pinyin(username)
return englishname
except Exception as e:
print(f"获取用户信息失败: {e}")
return None
#获取当前打开的窗口的进程
def get_window_processes():
try:
# 使用wmctl获取窗口列表
output = subprocess.check_output(['wmctrl', '-lp']).decode('utf-8')
window_pids = []
for line in output.splitlines():
parts = line.split()
if len(parts) >= 3:
window_pids.append(int(parts[2]))
return list(set(window_pids)) # 去重
except FileNotFoundError:
print("请先安装wmctrl: sudo apt install wmctrl")
return []
# 判断incam是否在运行
def is_process_running():
"""
判断InCAMPro进程是否正在运行
返回:
bool: True表示进程正在运行,False表示未运行
"""
window_pids = get_window_processes()
if not window_pids:
print("无法获取窗口进程信息")
return False
for proc in psutil.process_iter(['pid', 'name']):
if proc.info['pid'] in window_pids and proc.info['name'].lower() == 'incampro':
return True
return False
# 获取incam的pid
def get_incampro_pid():
"""
获取InCAMPro进程的PID
返回:
int/None: 返回进程PID(如果找到),否则返回None
"""
window_pids = get_window_processes()
if not window_pids:
print("无法获取窗口进程信息")
return None
for proc in psutil.process_iter(['pid', 'name']):
if proc.info['pid'] in window_pids and proc.info['name'].lower() == 'incampro':
return proc.info['pid'] # 返回找到的PID
return None # 没有找到则返回None
# FUNCTION__
class Child_window(QDialog):
"""
料号输入子窗口
"""
def __init__(self, parent=None):
super().__init__(parent)
self.child_ui1 = job_input_ui()
self.child_ui1.setupUi(self)
# 添加确认按钮信号连接
self.child_ui1.confirm_btn.clicked.connect(self.confirm_input)
self.material_number = None # 存储输入的料号
def get_material_number(self):
return self.child_ui1.input_job.text().strip()
def confirm_input(self):
"""验证输入并关闭窗口"""
if self.get_material_number():
self.accept() # 关闭对话框并返回QDialog.Accepted
else:
QMessageBox.warning(self, "错误", "请输入有效料号")
class ChildWindow2(QDialog):
"""层名选择对话框"""
def __init__(self, layer_list, parent=None):
super().__init__(parent)
self.child_ui2 = get_layer_ui()
self.child_ui2.setupUi(self, layer_list)
# 初始化下拉框
self.child_ui2.get_layer.addItems(layer_list)
if layer_list:
self.child_ui2.get_layer.setCurrentIndex(0)
# 连接信号
self.child_ui2.btn_ok.clicked.connect(self.accept)
self.child_ui2.btn_ok.clicked.connect(self.store_selection)
self.selected_layer = None
def store_selection(self):
"""存储选择的层名"""
self.selected_layer = self.child_ui2.get_layer.currentText()
def get_selected_layer(self):
"""获取选择的层名"""
return self.selected_layer
class ChildWindow3(QDialog):
"""
画圆子窗口
"""
def __init__(self, parent=None):
super().__init__(parent)
self.child_ui3 = draw_circle_ui()
self.child_ui3.setupUi(self)
# 添加确认按钮信号连接
self.child_ui3.sure_btn.clicked.connect(self.accept)
self.value = None
def get_circle_data(self):
"""获取输入的圆参数"""
try:
x = float(self.child_ui3.x_input.text())
y = float(self.child_ui3.y_input.text())
sym = self.child_ui3.sym_input.text() # 保持为字符串
if not sym: # 检查是否为空
return None
return (x, y, sym)
except ValueError:
return None
class ComTestQT(QWidget):
"""
PYQT界面实现对应按钮操作incam动作
"""
def __init__(self): # def __init__(self, pid=None, JOB=None, STEP=None):
super(ComTestQT, self).__init__()
self.main_ui = inerfaceui()
self.main_ui.setupUi(self)
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.JOB = os.environ.get('JOB', None)
# print(f"JOB: {self.JOB}")
# print("111111111111111111111111111111")
self.STEP = os.environ.get('STEP', None)
# --启动pycharm.sh时,里面有export INCAM_DEBUG=yes设置此环境变量
INCAM_DEBUG = os.getenv('INCAM_DEBUG', None)
# 接口定义
# if INCAM_DEBUG == 'yes':
# 通过genesis gateway命令连结pid进行会话,不用在genesis环境下运行,直接用gateway的方式,可在pycharm环境下直接debug
self.incam = Gateway()
# 方法genesis_connect通过查询log-genesis文件获取的料号名
# self.JOB = self.incam.job_name
# self.STEP = self.incam.step_name
# self.pid = self.incam.pid
# else:
# self.incam = InCAM()
# self.pid = os.getpid()
self.ico = ICO(incam=self.incam)
# self.icNet = ICNET(incam=self.incam)
# # self.eqHelper = EqHelper(self.incam, self.JOB, self.STEP)
# print(f"没有debug:{self.JOB}")
# self.jobName = self.ico.SimplifyJobName(jobName=self.JOB)
# self.dbSite = self.ico.GetDBSite(JOB=self.JOB)
# self.SITE = self.ico.GetSite(JOB=self.JOB)
# layerMatrix = self.ico.GetLayerMatrix()
# self.incam.COM("get_user_group")
# self.uGroup = self.incam.COMANS
# self.incam.COM('get_user_name')
# self.userName = self.incam.COMANS
# self.incam.gateway_connect()
self.child_window = None
self.child_window2 = None
self.child_window3 = None
self.slot_func()
# 全局引用自己,供 FastAPI 使用
global qt_app_instance
qt_app_instance = self
def slot_func(self):
"""
统一定义槽函数
:return:
"""
self.main_ui.open_incam.clicked.connect(self.start_incam)
self.main_ui.open_job.clicked.connect(self.show_child_window)
self.main_ui.get_layername.clicked.connect(self.show_layer_dialog)
self.main_ui.draw_circle.clicked.connect(self.show_draw_circle_dialog)
# 连接信号到实际方法
api_signals.open_incam_signal.connect(self.start_incam)
api_signals.open_job_signal.connect(self.show_child_window)
api_signals.get_layername_signal.connect(self.show_layer_dialog)
api_signals.draw_circle_signal.connect(self.show_draw_circle_dialog)
########运行incam程序######
def start_incam(self):
"""
尝试通过终端命令启动 incam 或 pro
"""
if is_process_running():
print("已有InCAMPro进程正在运行,无法再次启动")
# messageBox("Incam 已在运行")
return
else:
try:
self.file_path = "/home/genesis/.incam/login"
self.file_path_enc = "/home/genesis/.incam/login.enc"
account = get_username()#"songwenhua"#self.inputNewAccount.text().strip()
password = "scc123456"#self.inputNewPassword.text().strip()
if os.path.exists(self.file_path_enc):
os.remove(self.file_path_enc)
with open(self.file_path, "w", encoding="utf-8") as f:
f.writelines('\n'.join((account, password)))
home = os.path.expanduser("~") # 获取家目录路径
# subprocess.Popen(['gnome-terminal', '--working-directory', home, '--', 'bash', '-ic', 'incam; exec bash'])
subprocess.Popen(['gnome-terminal', '--working-directory', home, '--', 'bash', '-ic', 'pro; exec bash'])
print("Incam 已作为后台进程启动")
# while not is_process_running():
time.sleep(10)
if os.path.exists(self.file_path):
os.remove(self.file_path)
except FileNotFoundError:
print("'gnome-terminal' 未找到,请安装它:sudo apt install gnome-terminal")
except Exception as e:
print("启动 incam 时发生异常:", str(e))
def show_child_window(self):
"""显示子窗口并等待输入"""
# 重置子窗口状态
if self.child_window is None:
self.child_window = Child_window(self)
self.child_window.child_ui1.input_job.clear()
if self.child_window.exec_() == QDialog.Accepted:
short_name = self.child_window.get_material_number()
self.open_JOB(short_name)
def ensure_incam_ready(self):
"""确保 incam 和 ico 都已准备就绪"""
# if self.incam is None:
self.incam = Gateway()
self.ico = ICO(incam=self.incam)
# else:
# pid = get_incampro_pid()
# print("22222222222222222222222")
if not hasattr(self.incam, 'pid'):
print("44444")
# if pid is None:
if not self.incam.pid:
print("pid is None")
return False
# if self.ico is None:
# self.ico = ICO(incam=self.incam)
print("kkkkkkkkk")
return True
def open_JOB(self, short_name=None):
"""
根据料号简称打开对应的全称JOB
参数:
material_number (str): 要打开的料号,如果为None则使用当前JOB
返回:
bool: True表示打开成功,False表示失败
"""
try:
# 1. 检查InCAMPro进程
pid = get_incampro_pid()
if pid is None:
QMessageBox.warning(self, "警告", "请先启动InCAMPro")
return False
# 初始化Gateway连接
# 等待 Gateway 真正准备好
print(f"pid11: {pid}")
if not self.ensure_incam_ready():
print(f"pid: {pid}")
QMessageBox.warning(self, "警告", "Gateway连接失败")
return False
# ----------------------------------------------
# print("111111111111111111111111111111111111111")
# print(f"pid: {pid}")
# # if not self.incam.pid:
# # self.incam.connect(pid)
# print("self.incam.pid:", self.incam.pid)
# print("nnnnn: ",hasattr(self.incam, 'pid'))
# # self.incam.pid = get_incampro_pid()
# if not hasattr(self.incam, 'pid') or not self.incam.pid:
# QMessageBox.warning(self, "警告", "Gateway连接失败")
# return False
# 获取JOB列表并验证料号
job_list = self.ico.GetJOBlist()
if not job_list:
QMessageBox.warning(self, "警告", "没有可用的JOB列表")
return False
# 查找匹配的全称料号
full_name = None
for JOB in job_list:
simplified = self.ico.SimplifyJobName(jobName=JOB)
if simplified == short_name:
full_name = JOB
break
if not full_name:
QMessageBox.warning(self, "错误", f"未找到匹配全称的料号: {short_name}")
return False
# 打开全称料号
self.incam.COM(f'open_job,job={full_name},open_win=yes')
self.JOB = full_name
if self.ico.isJobOpen(full_name):
QMessageBox.information(self, "成功", f"已打开料号: {full_name}")
# return True
else:
QMessageBox.warning(self, "错误", f"打开料号失败: {full_name}")
# return False
except Exception as e:
QMessageBox.critical(self, "异常", f"处理料号时出错: {str(e)}")
# return False
def show_layer_dialog(self):
"""显示层名选择对话框"""
try:
# 验证前置条件
# jobName=self.JOB
print(f"JOB value before check: {self.JOB}")#已经得到了准确的job名称
if not self.check_preconditions():
return
# 获取层名列表
# print(f"JOB v: {self.JOB}")
layer_list = self.ico.GetLayerList()
if not layer_list:
QMessageBox.warning(self, "警告", "没有可用的Layer列表")
return
# 创建并显示对话框
dialog2 = ChildWindow2(layer_list, self)
if dialog2.exec_() == QDialog.Accepted:
selected_layer = dialog2.get_selected_layer()
self.handle_selected_layer(selected_layer)
except Exception as e:
QMessageBox.critical(self, "错误", f"操作失败: {str(e)}")
def check_preconditions(self):
"""检查必要前提条件"""
if get_incampro_pid() is None:
QMessageBox.warning(self, "警告", "请先启动InCAMPro")
return False
# 尝试获取当前激活的JOB
try:
if self.incam.job_name:
self.JOB = self.incam.job_name
# print(f"从incam获取的JOB: {self.JOB}")
QMessageBox.information(self, "成功", f"从incam获取的JOB: {self.JOB}")
except Exception as e:
# print(f"获取incam job_name失败: {str(e)}")
QMessageBox.warning(f"获取incam job_name失败: {str(e)}")
# 检查JOB是否有效
if not self.JOB:
QMessageBox.warning(self, "警告", "请打开JOB后再操作")
return False
# 尝试获取layer列表验证JOB有效性
try:
self.ico.JOB= self.JOB
layer_list = self.ico.GetLayerList()
# print(f"获取到的层列表: {layer_list}")
if not layer_list:
QMessageBox.warning(self, "警告", "获取到的层列表为空")
return False
return True
except Exception as e:
QMessageBox.warning(self, "警告", f"获取层列表失败: {str(e)}")
return False
def handle_selected_layer(self, layer_name):
"""处理选择的层名"""
print(f"选择的层名: {layer_name}")
self.LAYER = layer_name
QMessageBox.information(self, "成功", f"已选择层名: {layer_name}")
def show_draw_circle_dialog(self):
"""显示画圆参数对话框"""
if not self.check_preconditions():
return
if not hasattr(self, 'LAYER') or not self.LAYER:
QMessageBox.warning(self, "警告", "请先选择层")
return
dialog3 = ChildWindow3(self)
if dialog3.exec_() == QDialog.Accepted:
params = dialog3.get_circle_data()
# print(f"获取的参数: {params}")
if params:
self.draw_circle_on_layer(*params)
else:
QMessageBox.warning(self, "警告", "请输入有效的数字参数")
def draw_circle_on_layer(self, center_x, center_y, symbol):
"""在指定层上画圆"""
try:
self.ico.ClearAll()
layer_name=self.LAYER
self.ico.DispWork(layer=layer_name)
self.ico.AddPad(
x = center_x,
y = center_y,
sym = symbol
)
self.ico.ClearLayer()
QMessageBox.information(self, "成功", f"已在层 {self.LAYER} 上绘制圆形")
except Exception as e:
QMessageBox.critical(self, "错误", f"画圆时出错: {str(e)}")
########## FastAPI 部分 ###########
class ApiSignalEmitter(QObject):
open_incam_signal = pyqtSignal()
open_job_signal = pyqtSignal()
get_layername_signal = pyqtSignal()
draw_circle_signal = pyqtSignal()
api_signals = ApiSignalEmitter()
# app = FastAPI(title="InCAM Remote Control API", description="通过HTTP控制InCAM自动化任务")
app = FastAPI(title="InCAM 控制 API", version="1.0")
@app.post("/api/open_incam")
async def api_open_incam():
# qt_app_instance.start_incam()
# 不再直接调用 myapp.open_job()
# 而是发送信号给主线程处理
api_signals.open_incam_signal.emit()
return {"status": "success", "action": "open_incam", "message": "InCAM已启动"}
@app.post("/api/open_job")
async def api_open_job():
# qt_app_instance.show_child_window()
api_signals.open_job_signal.emit()
return {"status": "success", "action": "open_job", "message": "Job窗口已打开"}
@app.post("/api/get_layername")
async def api_get_layername():
# qt_app_instance.show_layer_dialog()
api_signals.get_layername_signal.emit()
return {"status": "success", "action": "get_layername", "message": "层名获取成功"}
@app.post("/api/draw_circle")
async def api_draw_circle():
# qt_app_instance.show_draw_circle_dialog()
api_signals.draw_circle_signal.emit()
return {"status": "success", "action": "draw_circle", "message": "圆圈绘制完成"}
@app.get("/")
async def root():
return {
"message": "InCAM 控制 API 运行中",
"endpoints": [
"/api/open_incam",
"/api/open_job",
"/api/get_layername",
"/api/draw_circle"
],
"method": "POST"
}
# @app.get("/")
# async def root():
# if qt_app_instance is None:
# return {"status": "error", "message": "Qt app not initialized"}
# return {
# "status": "running",
# "service": "InCAM Remote Control API",
# "api_docs": "/docs",
# "available_endpoints": [
# "/api/open_incam",
# "/api/open_job",
# "/api/get_layername",
# "/api/draw_circle"
# ]
# }
def run_fastapi():
"""在后台线程运行 FastAPI"""
uvicorn.run(app, host="127.0.0.1", port=8000, log_level="info")
# def run_fastapi():
# """在后台线程运行 FastAPI"""
# config = uvicorn.Config(
# app=app,
# host="127.0.0.1",
# port=8000,
# log_level="info",
# lifespan="on" # 显式开启 lifespan 支持
# )
# server = uvicorn.Server(config)
# server.run()
qt_app_instance = None
if __name__ == '__main__':
qapp = QApplication(sys.argv)
myapp = ComTestQT()
myapp.show()
print("PyQt5 GUI 已显示,将在1秒后启动 FastAPI...")
# 在子线程中启动 FastAPI
QTimer.singleShot(
1000,
lambda: threading.Thread(target=run_fastapi, daemon=True).start()
)
sys.exit(qapp.exec_())
# EXIT__
以上是play.py的全部代码,并没有内联Uvicorn 的启动逻辑。
最新发布