AOI/SPI等检测设备对接MES系统插件化架构设计
一、项目背景
该项目是一个基于Python和PyQt5开发的制造执行系统(MES)集成工具,为解决 AOI/SPI 等检测设备与不同客户 MES 系统对接难的问题,设计并开发了一套插件化中间件工具。该系统采用插件化架构设计,支持200+客户的定制化需求,已在多家制造企业的生产环境中稳定运行。
二、架构设计理念
2.1 核心设计原则
- 开闭原则:对扩展开放,对修改封闭
- 依赖倒置:高层模块不依赖低层模块,都依赖抽象
- 单一职责:每个插件专注于特定客户的业务逻辑
- 接口隔离:定义清晰的扩展点接口
2.2 架构分层
┌─────────────────────────────────────┐
│ Presentation Layer (PyQt5 UI) │
├─────────────────────────────────────┤
│ Service Layer (Socket/HTTP) │
├─────────────────────────────────────┤
│ Plugin Layer (Custom Engines) │
├─────────────────────────────────────┤
│ Core Layer (BaseEngine) │
├─────────────────────────────────────┤
│ Data Layer (Entity/VO) │
└─────────────────────────────────────┘
三、插件化核心实现
3.1 抽象基类设计
class BaseEngine(metaclass=abc.ABCMeta):
"""插件基类,定义所有扩展点"""
@abc.abstractmethod
def send_data_to_mes(self, data_vo: DataVo, other_data: dict, other_param: Any) -> dict:
"""发送数据到MES"""
pass
@abc.abstractmethod
def check_sn(self, other_vo: OtherVo, main_window: Any) -> dict:
"""条码校验"""
pass
# 其他扩展点...
3.2 插件目录结构
services/
├── default/ # 默认实现
│ └── main.py
├── custom/ # 客户定制实现
│ ├── customer1/ # xx定制
│ │ └── main.py
│ ├── customer2/ # xx定制
│ │ ├── main.py
│ │ ├── iot_data_collector.py
│ │ └── xmodule.py
│ ├── standard_en/ # 标准英文版
│ │ └── main.py
│ └── ... # 200+客户目录
四、扩展点设计
4.1 核心扩展点
| 扩展点 | 功能描述 | 使用场景 |
|---|---|---|
| send_data_to_mes | 发送检测数据到MES | 所有客户 |
| check_sn | 条码校验 | 需要条码验证的客户 |
| send_device_status_to_mes | 设备状态上报 | 需要状态监控的客户 |
| get_sn_by_mes | 从MES获取条码 | 需要MES下发条码的客户 |
| custom_cron_function | 定时任务 | 需要定时处理的客户 |
4.2 钩子函数
class BaseEngine:
def init_main_window(self, main_window, other_vo: OtherVo):
"""初始化主窗口钩子"""
pass
def save_btn_on_window(self, main_window):
"""保存按钮钩子"""
pass
def combo_index_changed(self, combo_vo: ComboVo, main_window: Any):
"""下拉框改变钩子"""
pass
五、配置驱动设计
5.1 UI配置
class Engine(ErrorMapEngine):
# 表单配置
form = {
"username": {
"ui_name": "用户名",
"value": "admin"
}
}
# 下拉框配置
combo = {
"device_type": {
"item": ["AOI", "SPI"],
"value": "AOI",
"ui_name": "机台类型"
}
}
# 按钮配置
button = {
"test_connect": {
"ui_name": "测试连接"
}
}
5.2 配置持久化
# 配置文件位置:~/.config/mesconfig.json
{
"form": {
"username": {"value": "admin"}
},
"combo": {
"device_type": {"value": "AOI"}
},
"app_setting": {
"merge_send": false,
"send_error_info": true
}
}
六、数据流设计
6.1 数据实体
class PcbEntity:
"""整板实体"""
def __init__(self, pcb_info: dict, board_data: dict):
self.pcb_barcode = pcb_info.get("pcb_barcode")
self.project_name = pcb_info.get("project_name")
self.final_result = pcb_info.get("final_result")
def yield_board_entity(self):
"""生成拼板实体"""
for board_item in self.board_data:
yield BoardEntity(board_item, self)
6.2 数据访问对象
class DataVo:
"""数据访问层"""
def __init__(self, pcb_info: dict, board_data: dict, config_data: dict):
self.pcb_entity = PcbEntity(pcb_info, board_data)
self.all_config_json = ChainMap(
config_data.get("combo", {}),
config_data.get("form", {}),
config_data.get("path", {})
)
def get_value_by_cons_key(self, cons_key):
"""获取配置参数"""
return self.all_config_json.get(cons_key, {}).get("value")
七、通信架构
7.1 Socket服务
class SocketThread(QThread):
def run(self):
port = engine.mes_config_port
self.sk.bind(("0.0.0.0", port))
self.sk.listen()
while True:
conn, addr = self.sk.accept()
data = conn.recv(4086)
json_data = json.loads(data.decode("utf-8"))
# 路由到对应的插件方法
if "ReviewPath" in json_data:
ret_data = self.send_data_to_mes(json_data)
else:
ret_data = self.call_other_function(json_data)
7.2 HTTP服务
class HttpService:
def handle_request(self, request):
"""处理HTTP请求"""
func_name = request.get("funcName")
if func_name == "CheckBarcode":
return engine.check_sn(...)
elif func_name == "SendDeviceStatus":
return engine.send_device_status_to_mes(...)
八、设计模式应用
8.1 模板方法模式
class ErrorMapEngine(BaseEngine):
"""错误码映射引擎"""
ERROR_MAP = {
"1": {"standard": "漏件", "custom_code": "1"},
"2": {"standard": "错件", "custom_code": "2"}
}
def send_data_to_mes(self, data_vo, other_data, other_param):
# 模板方法:定义算法骨架
pcb_entity = data_vo.pcb_entity
# 1. 数据转换
transformed_data = self.transform_data(pcb_entity)
# 2. 错误码映射
mapped_data = self.map_error_code(transformed_data)
# 3. 发送到MES
return self.send_to_mes(mapped_data)
8.2 策略模式
# 不同客户使用不同的数据发送策略
class XXX1Engine(ErrorMapEngine):
def send_data_to_mes(self, data_vo, other_data, other_param):
# customer1特定的发送策略
return self._send_xml_format(data_vo)
class XXX2Engine(ErrorMapEngine):
def send_data_to_mes(self, data_vo, other_data, other_param):
# customer2特定的发送策略
return self._send_json_format(data_vo)
九、实际案例分析
9.1 客户1定制案例
class Engine(ErrorMapEngine):
"""客户1定制"""
version = {
"customer": ["客户1", "customer1"],
"feature": ["相机扫码校验", "板式切换", "发送数据到MES"]
}
def check_project_by_barcode(self, other_vo: OtherVo, param: dict):
"""板式切换的条码校验"""
pcb_barcode = param.get("barcode")
resp = self._send_event(MesEvent.PART_RECEIVED, INSPECTOR)
if resp.get('partForStation') == 'true':
if resp.get('changeOver') == 'true':
# 执行板式切换
return self.switch_program(INSPECTOR, True)
9.2 客户2定制案例
class Engine(ErrorMapEngine):
"""客户2定制"""
version = {
"customer": ["客户2", "customer2"],
"feature": ["从mes获取条码", "条码校验", "上传数据", "数采功能"]
}
def __init__(self):
self.iot_data_collector = IoTDataCollector()
def send_data_to_mes(self, data_vo, other_data, other_param):
# 1. 发送检测数据
result = self._send_detection_data(data_vo)
# 2. 上传图片
if self._should_upload_image():
self._upload_images(data_vo)
# 3. 发送IoT数据
if self.iot_data_collector:
self.iot_data_collector.send_data(data_vo)
return result
十、优势与挑战
10.1 架构优势
- 高扩展性:新增客户只需创建新的插件,无需修改核心代码
- 易维护性:每个客户逻辑独立,互不影响
- 代码复用:通过继承机制,客户可以复用基础功能
- 灵活配置:通过配置文件驱动,减少硬编码
10.2 面临挑战
- 版本管理:200+插件的版本同步和兼容性管理
- 测试复杂度:每个插件都需要独立测试
- 性能优化:动态加载可能影响启动性能
- 文档维护:保持插件文档的更新
十一、最佳实践
11.1 插件开发规范
class Engine(ErrorMapEngine):
version = {
"customer": ["客户中文名", "客户英文名"],
"version": "release v1.0.0.1",
"device": "设备型号",
"feature": ["功能列表"],
"author": "开发者",
"release": """
date: 2023-04-25 11:07 init
date: 2023-04-26 10:00 新增功能A
"""
}
11.2 错误处理
def send_data_to_mes(self, data_vo, other_data, other_param):
try:
# 业务逻辑
result = self._do_send(data_vo)
return self.x_response("true", "发送成功")
except Exception as e:
self.log.error(f"发送失败: {e}")
return self.x_response("false", f"发送失败: {str(e)}")
十二、总结
该MES配置器通过插件化架构设计,成功实现了对200+不同客户需求的支持。核心设计思想包括:
- 基于抽象的插件机制:通过BaseEngine定义扩展点,客户通过继承实现定制逻辑
- 配置驱动的UI生成:通过form、combo等配置动态生成界面
- 清晰的分层架构:表现层、服务层、插件层、核心层、数据层各司其职
- 灵活的扩展点设计:提供丰富的钩子函数和扩展方法
- 标准化的数据流:通过Entity和VO对象规范数据传递
这种架构设计不仅满足了当前的业务需求,也为未来的扩展和维护奠定了良好的基础。通过插件化,系统能够快速响应客户的定制化需求,同时保持核心代码的稳定性和可维护性。

浙公网安备 33010602011771号