Files
oapy/labs/src/lab17_5_threadusemodel.py

327 lines
11 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
"""
Lab 17-5: 线程使用模型 (Thread Use Model)
本示例演示 OpenAccess 的线程使用模型
注意这里不使用多线程仅展示如何设置和重置线程模型
线程模型类型
- oacSingleThreadUseModel: 单线程模式
- oacMultipleReadersThreadUseModel: 多读线程模式只读
- oacMultipleWritersThreadUseModel: 多写线程模式
"""
import os
import sys
import shutil
# 导入工具函数和 OA 绑定
from utils import init_oa, c_str, make_oa_string, make_oa_name, get_namespace
from oapy._oa import _base, _dm, _design
# ============================================================================
# 全局配置
# ============================================================================
class Globals:
"""全局配置类,管理库路径和名称"""
_instance = None
@classmethod
def get_instance(cls):
"""获取全局单例"""
if cls._instance is None:
cls._instance = cls()
return cls._instance
def __init__(self):
"""初始化全局配置"""
# 初始化设计数据库
_design.oaDesignInit()
ns = get_namespace("native")
# 库路径和名称
self.lib_path_str = "../data/LibDir"
self.lib_name_str = "LibDir"
self.cell_name_str = "Cell"
self.view_name_str = "View"
self.sn_lib = make_oa_name(ns, self.lib_name_str)
self.sn_cell = make_oa_name(ns, self.cell_name_str)
self.sn_view = make_oa_name(ns, self.view_name_str)
self.str_lib_path = make_oa_string(self.lib_path_str)
# 网络名称
self.sn_net1 = make_oa_name(ns, "Net1")
self.sn_net2 = make_oa_name(ns, "Net2")
self.sn_net3 = make_oa_name(ns, "Net3")
# 获取视图类型(原理图)
self.view_type = _dm.oaViewType.get(_dm.oaReservedViewType(_dm.oaReservedViewTypeEnum.oacSchematic))
# 获取构建版本号
packages = _base.oaBuildInfoArray()
_base.oaBuildInfo.getPackages(packages)
self.build_number = packages[0].getBuildNumber()
print(f"\n*** 使用 OA 构建版本: {self.build_number} ***\n")
# ============================================================================
# 线程模型管理函数
# ============================================================================
def log_setting_thread_use_model(new_model):
"""记录线程模型设置操作"""
model_name = new_model.getName()
print(f"session.setThreadUseModel({model_name})")
def set_thread_use_model(new_model_enum):
"""
设置线程使用模型
参数:
new_model_enum: oaThreadUseModelEnum 枚举值
"""
session = _base.oaSession.get()
current_model = session.getThreadUseModel()
new_model = _base.oaThreadUseModel(new_model_enum)
print(f"\n切换线程模型: {current_model.getName()} -> {new_model.getName()}")
# 特殊处理OA 22.41p004 版本的限制
# 从 MultipleReaders 切换到其他模型时,必须先回到 SingleThread
OA22_41_p004 = 25346
globals_obj = Globals.get_instance()
if globals_obj.build_number == OA22_41_p004:
if (new_model_enum == _base.oaThreadUseModelEnum.oacMultipleReadersThreadUseModel and
new_model_enum != _base.oaThreadUseModelEnum.oacSingleThreadUseModel):
print("\n[警告] OA 22.41p004 版本限制:")
print(" 从 MultipleReaders 切换到其他模型时,必须先回到 SingleThread")
print(" 否则 API 会抛出 oacInternalError 异常")
# 先切换到单线程模式
log_setting_thread_use_model(_base.oaThreadUseModel(_base.oaThreadUseModelEnum.oacSingleThreadUseModel))
session.setThreadUseModel(_base.oaThreadUseModel(_base.oaThreadUseModelEnum.oacSingleThreadUseModel))
# 设置新的线程模型
log_setting_thread_use_model(new_model)
session.setThreadUseModel(new_model)
# 确认当前模型
final_model = session.getThreadUseModel()
print(f"当前线程模型: {final_model.getName()}\n")
# ============================================================================
# 设计操作函数
# ============================================================================
def create_original_designs():
"""创建初始设计(库、单元、视图和网络)"""
globals_obj = Globals.get_instance()
# 清理并创建库目录
if os.path.exists(globals_obj.lib_path_str):
shutil.rmtree(globals_obj.lib_path_str)
os.makedirs(globals_obj.lib_path_str, exist_ok=True)
# 创建库(共享模式,使用 DMFileSys 插件)
lib = _dm.oaLib.create(
globals_obj.sn_lib,
make_oa_string(globals_obj.lib_path_str),
_dm.oaLibMode(_dm.oaLibModeEnum.oacSharedLibMode),
make_oa_string("oaDMFileSys"),
_dm.oaDMAttrArray(0)
)
# 打开设计进行写入
design = _design.oaDesign.open(
globals_obj.sn_lib,
globals_obj.sn_cell,
globals_obj.sn_view,
globals_obj.view_type,
'w'
)
assert design.isValid(), "设计打开失败"
# 创建顶层 block
block = _design.oaBlock.create(design, True)
# 创建三个标量网络
net1 = _design.oaScalarNet.create(block, globals_obj.sn_net1)
net2 = _design.oaScalarNet.create(block, globals_obj.sn_net2)
net3 = _design.oaScalarNet.create(block, globals_obj.sn_net3)
print(f"\n已创建网络: {net1.getName()}, {net2.getName()}, {net3.getName()}")
# 保存设计
print("\n保存设计...")
design.save()
def count_invalid_nets(block):
"""
统计无效的 net 数量
参数:
block: 要检查的 block
返回:
无效 net 的数量
"""
invalid_count = 0
nets = block.getNets()
for net in nets:
if not net.isValid():
invalid_count += 1
return invalid_count
def modify_designs():
"""
修改设计创建新网络
行为取决于当前线程模型
- MultipleReaders 模式尝试创建网络会失败设计为只读
- 其他模式正常创建网络
"""
globals_obj = Globals.get_instance()
# 查找已存在的设计
design = _design.oaDesign.find(
globals_obj.sn_lib,
globals_obj.sn_cell,
globals_obj.sn_view
)
assert design.isValid(), "设计查找失败"
# 获取顶层 block
block = design.getTopBlock()
print("在设计中创建新标量网络...")
session = _base.oaSession.get()
current_model = session.getThreadUseModel()
if current_model == _base.oaThreadUseModelEnum.oacMultipleReadersThreadUseModel:
# MultipleReaders 模式下,设计为只读
print("\n[警告] 当前为 MultipleReaders 模式,设计为只读")
print(" 尝试修改设计会导致未定义行为")
print(" 在 debug 模式下会抛出 oacInternalError 异常")
print(" 在 opt 模式下可能创建无效对象")
# 统计修改前的状态
before_invalid = count_invalid_nets(block)
before_total = block.getNets().getCount()
# 尝试创建网络(预期会失败)
try:
new_net = _design.oaScalarNet.create(block)
print(" [注意] 网络创建成功(可能是 opt 模式)")
except _base.oaException as e:
print(f" [预期] 创建失败: {e.getMsg()}")
# 统计修改后的状态
after_invalid = count_invalid_nets(block)
after_total = block.getNets().getCount()
print(f" 修改前: {before_total} 个网络 ({before_invalid} 个无效)")
print(f" 修改后: {after_total} 个网络 ({after_invalid} 个无效)")
else:
# 其他模式下可以正常创建网络
print(f" 当前模型: {current_model.getName()},可以创建网络")
new_net = _design.oaScalarNet.create(block)
assert new_net.isValid(), "新网络创建失败"
print(f" 成功创建网络: {c_str(new_net.getName())}")
# ============================================================================
# 测试函数
# ============================================================================
def test_thread_use_model():
"""
测试不同的线程使用模型
测试流程
1. SingleThread 模式创建初始设计
2. MultipleReaders 模式尝试修改预期失败
3. MultipleWriters 模式修改设计
4. 多次切换模型并修改
"""
print("=" * 70)
print("测试线程使用模型")
print("=" * 70)
# 测试 1: 单线程模式 - 创建初始设计
print("\n[测试 1] 单线程模式 - 创建初始设计")
set_thread_use_model(_base.oaThreadUseModelEnum.oacSingleThreadUseModel)
create_original_designs()
# 测试 2: 多读模式 - 尝试修改(应该失败)
print("\n[测试 2] 多读模式 - 尝试修改设计")
set_thread_use_model(_base.oaThreadUseModelEnum.oacMultipleReadersThreadUseModel)
modify_designs()
# 测试 3: 多写模式 - 可以修改
print("\n[测试 3] 多写模式 - 修改设计")
set_thread_use_model(_base.oaThreadUseModelEnum.oacMultipleWritersThreadUseModel)
set_thread_use_model(_base.oaThreadUseModelEnum.oacMultipleWritersThreadUseModel) # 重复设置测试
modify_designs()
# 测试 4: 切换回多读模式
print("\n[测试 4] 多读模式 - 再次尝试修改")
set_thread_use_model(_base.oaThreadUseModelEnum.oacMultipleReadersThreadUseModel)
set_thread_use_model(_base.oaThreadUseModelEnum.oacMultipleReadersThreadUseModel) # 重复设置测试
modify_designs()
# 测试 5: 回到单线程模式
print("\n[测试 5] 单线程模式 - 最终修改")
set_thread_use_model(_base.oaThreadUseModelEnum.oacSingleThreadUseModel)
modify_designs()
print("\n" + "=" * 70)
print("线程模型测试完成")
print("=" * 70)
# ============================================================================
# 主程序
# ============================================================================
def main():
"""主函数"""
print("\n" + "=" * 70)
print("Lab 17-5: OpenAccess 线程使用模型演示")
print("=" * 70)
# 初始化 OA 子系统
init_oa()
try:
# 运行测试
test_thread_use_model()
print("\n........正常终止........\n")
return 0
except Exception as ex:
print(f"\n[错误] 未预期的异常: {ex}")
import traceback
traceback.print_exc()
return 1
if __name__ == "__main__":
sys.exit(main())