osis-python 0.5.1__tar.gz → 0.5.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. {osis_python-0.5.1/src/osis_python.egg-info → osis_python-0.5.2}/PKG-INFO +1 -1
  2. {osis_python-0.5.1 → osis_python-0.5.2/src/osis_python.egg-info}/PKG-INFO +1 -1
  3. {osis_python-0.5.1 → osis_python-0.5.2}/src/osis_python.egg-info/SOURCES.txt +4 -1
  4. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/__init__.py +1 -1
  5. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/boundary/manager.py +13 -1
  6. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/__init__.py +2 -0
  7. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/client.py +11 -2
  8. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/command.py +11 -2
  9. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/engine.py +8 -4
  10. osis_python-0.5.2/src/pyosis/core/exceptions.py +32 -0
  11. osis_python-0.5.2/src/pyosis/core/references.py +87 -0
  12. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/dynamic/manager.py +2 -2
  13. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/element/manager.py +13 -1
  14. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/general/interface.py +15 -19
  15. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/geometry/manager.py +8 -0
  16. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/live/manager.py +15 -0
  17. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/load/manager.py +34 -0
  18. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/load/tendon.py +18 -0
  19. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/material/manager.py +8 -1
  20. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/node/manager.py +8 -0
  21. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/manager.py +49 -4
  22. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/manager.py +36 -7
  23. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/param.py +17 -8
  24. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/thickness/manager.py +8 -0
  25. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/generator.py +3 -2
  26. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/out_to_python.py +99 -5
  27. osis_python-0.5.2/tests/test_delete_occupied.py +432 -0
  28. {osis_python-0.5.1 → osis_python-0.5.2}/LICENSE +0 -0
  29. {osis_python-0.5.1 → osis_python-0.5.2}/README.md +0 -0
  30. {osis_python-0.5.1 → osis_python-0.5.2}/pyproject.toml +0 -0
  31. {osis_python-0.5.1 → osis_python-0.5.2}/setup.cfg +0 -0
  32. {osis_python-0.5.1 → osis_python-0.5.2}/src/osis_python.egg-info/dependency_links.txt +0 -0
  33. {osis_python-0.5.1 → osis_python-0.5.2}/src/osis_python.egg-info/requires.txt +0 -0
  34. {osis_python-0.5.1 → osis_python-0.5.2}/src/osis_python.egg-info/top_level.txt +0 -0
  35. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/boundary/__init__.py +0 -0
  36. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/boundary/interface.py +0 -0
  37. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/control/__init__.py +0 -0
  38. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/control/control.py +0 -0
  39. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/control/io.py +0 -0
  40. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/control/manager.py +0 -0
  41. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/control/nonlinear.py +0 -0
  42. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/all_func.py +0 -0
  43. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/apdl_sync.py +0 -0
  44. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/basic_manager.py +0 -0
  45. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/build.py +0 -0
  46. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/core/sync_apdl_cli.py +0 -0
  47. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/dynamic/__init__.py +0 -0
  48. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/dynamic/load_to_mass.py +0 -0
  49. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/dynamic/modal.py +0 -0
  50. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/dynamic/seismic.py +0 -0
  51. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/element/__init__.py +0 -0
  52. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/element/interface.py +0 -0
  53. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/general/__init__.py +0 -0
  54. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/geometry/__init__.py +0 -0
  55. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/geometry/interface.py +0 -0
  56. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/live/__init__.py +0 -0
  57. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/live/analysis.py +0 -0
  58. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/live/grade.py +0 -0
  59. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/live/lane.py +0 -0
  60. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/load/__init__.py +0 -0
  61. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/load/loadcase.py +0 -0
  62. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/load/static.py +0 -0
  63. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/material/__init__.py +0 -0
  64. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/material/interface.py +0 -0
  65. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/node/__init__.py +0 -0
  66. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/node/interface.py +0 -0
  67. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/post/__init__.py +0 -0
  68. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/post/combine.py +0 -0
  69. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/post/design_check.py +0 -0
  70. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/post/display.py +0 -0
  71. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/post/manager.py +0 -0
  72. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/project/__init__.py +0 -0
  73. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/project/interface.py +0 -0
  74. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/project/manager.py +0 -0
  75. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/__init__.py +0 -0
  76. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/component_thickness.py +0 -0
  77. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/coordinate.py +0 -0
  78. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/creep_shrink.py +0 -0
  79. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/damping.py +0 -0
  80. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/property/pu_curve.py +0 -0
  81. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/quick_building/__init__.py +0 -0
  82. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/quick_building/interface.py +0 -0
  83. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/__init__.py +0 -0
  84. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/check.py +0 -0
  85. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/env.py +0 -0
  86. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/loadcase.py +0 -0
  87. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/manager.py +0 -0
  88. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/result/result.py +0 -0
  89. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/__init__.py +0 -0
  90. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/common.py +0 -0
  91. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/composite.py +0 -0
  92. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/numeric.py +0 -0
  93. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/rebar.py +0 -0
  94. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/rib.py +0 -0
  95. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/section/steel.py +0 -0
  96. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/settlement/__init__.py +0 -0
  97. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/settlement/interface.py +0 -0
  98. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/settlement/manager.py +0 -0
  99. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stability/__init__.py +0 -0
  100. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stability/buckling.py +0 -0
  101. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stability/manager.py +0 -0
  102. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stage/__init__.py +0 -0
  103. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stage/define.py +0 -0
  104. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stage/manager.py +0 -0
  105. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/stage/overall.py +0 -0
  106. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/thickness/__init__.py +0 -0
  107. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/thickness/interface.py +0 -0
  108. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/__init__.py +0 -0
  109. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/matrix.py +0 -0
  110. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/parser.py +0 -0
  111. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/routes.py +0 -0
  112. {osis_python-0.5.1 → osis_python-0.5.2}/src/pyosis/transfer/split.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osis-python
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: A Python client library for Osis APDL
5
5
  Author-email: "CCCC Highway Consultant Co. Ltd." <support@osisbim.com>
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: osis-python
3
- Version: 0.5.1
3
+ Version: 0.5.2
4
4
  Summary: A Python client library for Osis APDL
5
5
  Author-email: "CCCC Highway Consultant Co. Ltd." <support@osisbim.com>
6
6
  License-Expression: MIT
@@ -23,6 +23,8 @@ src/pyosis/core/build.py
23
23
  src/pyosis/core/client.py
24
24
  src/pyosis/core/command.py
25
25
  src/pyosis/core/engine.py
26
+ src/pyosis/core/exceptions.py
27
+ src/pyosis/core/references.py
26
28
  src/pyosis/core/sync_apdl_cli.py
27
29
  src/pyosis/dynamic/__init__.py
28
30
  src/pyosis/dynamic/load_to_mass.py
@@ -104,4 +106,5 @@ src/pyosis/transfer/matrix.py
104
106
  src/pyosis/transfer/out_to_python.py
105
107
  src/pyosis/transfer/parser.py
106
108
  src/pyosis/transfer/routes.py
107
- src/pyosis/transfer/split.py
109
+ src/pyosis/transfer/split.py
110
+ tests/test_delete_occupied.py
@@ -7,7 +7,7 @@ A library for extending OSIS functionality.
7
7
  A library that allows users to use CAE with great flexibility.
8
8
  """
9
9
 
10
- __version__ = "0.5.1"
10
+ __version__ = "0.5.2"
11
11
  __author__ = "CCCC Highway Consultant Co. Ltd." # 中交公路规划设计院 - 工程技术研究中心 - lzh - wjh - myf
12
12
 
13
13
  from .core.engine import OSISEngine
@@ -23,6 +23,7 @@ from typing import Any, Literal
23
23
  from enum import IntEnum
24
24
 
25
25
  from ..core.client import osis_client
26
+ from ..core import get_references, raise_if_occupied
26
27
  from .interface import (
27
28
  osis_boundary_general,
28
29
  osis_boundary_elstcspt,
@@ -638,6 +639,10 @@ class BoundaryManager:
638
639
  return 1
639
640
  return max(bd.no for bd in boundaries) + 1
640
641
 
642
+ def get_dependencies(self, no: int) -> dict[str, list]:
643
+ """查询边界被谁引用"""
644
+ return get_references("Boundary", no=no)
645
+
641
646
  # ── 增删改 ────────────────────────────────
642
647
  def create(self, no: int | None, type: str, *args: Any, **kwargs: Any) -> Boundary:
643
648
  """创建边界(便捷入口,内部转发到对应 create_* 方法)
@@ -846,7 +851,14 @@ class BoundaryManager:
846
851
  return self.get(no) # type: ignore[return-value]
847
852
 
848
853
  def delete(self, no: int) -> None:
849
- """删除边界"""
854
+ """删除边界
855
+
856
+ Raises:
857
+ DependencyError: 存在依赖项时
858
+ RuntimeError: 删除失败时抛出异常
859
+ """
860
+ deps = self.get_dependencies(no)
861
+ raise_if_occupied("Boundary", deps, no=no)
850
862
  ok, err = osis_boundary_del(no)
851
863
  if not ok:
852
864
  raise RuntimeError(f"删除边界 {no} 失败: {err}")
@@ -10,3 +10,5 @@ OSISEngine中的函数,将会分发到各个模块,被再次封装,意图
10
10
 
11
11
  from .command import REGISTRY, osis_run, set_run_mode
12
12
  from .client import set_osis_port, set_osis_url
13
+ from .exceptions import DependencyError
14
+ from .references import get_references, raise_if_occupied
@@ -26,6 +26,8 @@ session.mount("https://", adapter) # 如果你后续用HTTPS
26
26
 
27
27
  DEFAULT_PORT = 18080
28
28
  DEFAULT_URL = "http://localhost"
29
+ DEFAULT_TIMEOUT = 300
30
+ DEFAULT_SOLVE_TIMEOUT = 600
29
31
 
30
32
  def set_osis_url(url):
31
33
  '''
@@ -42,12 +44,19 @@ def set_osis_port(port):
42
44
  DEFAULT_PORT = port
43
45
 
44
46
  # ========== 通用客户端函数(复用连接池) ==========
45
- def osis_client(func_name: str, payload: dict, base_url=None) -> dict:
47
+ def osis_client(
48
+ func_name: str,
49
+ payload: dict,
50
+ base_url=None,
51
+ timeout: float | None = None,
52
+ ) -> dict:
46
53
  """
47
54
  通用 OSIS 接口客户端(复用连接池,高效批量调用)
48
55
  Args:
49
56
  func_name: C++ 接口名(如 "OSIS_Run")
50
57
  payload: 请求参数字典(自动序列化为 JSON)
58
+ base_url: url
59
+ timeout: HTTP 超时秒数,默认 DEFAULT_TIMEOUT
51
60
  Returns:
52
61
  异常时返回 (False, 错误信息)
53
62
  """
@@ -59,7 +68,7 @@ def osis_client(func_name: str, payload: dict, base_url=None) -> dict:
59
68
  url,
60
69
  data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
61
70
  headers={"Content-Type": "application/json; charset=utf-8"},
62
- timeout=300,
71
+ timeout=timeout if timeout is not None else DEFAULT_TIMEOUT,
63
72
  )
64
73
 
65
74
  data = None
@@ -19,7 +19,11 @@ from .client import osis_client
19
19
  # f.write(f"[{timestamp}] {text}\n")
20
20
  # f.close()
21
21
 
22
- def osis_run(strCmd: str="", mode: Literal["stash", "exec"]="exec") -> tuple[bool, str]:
22
+ def osis_run(
23
+ strCmd: str = "",
24
+ mode: Literal["stash", "exec"] = "exec",
25
+ timeout: float | None = None,
26
+ ) -> tuple[bool, str]:
23
27
  """
24
28
  http 模拟 C++ 原生 OSIS_Run 函数
25
29
  Args:
@@ -27,11 +31,16 @@ def osis_run(strCmd: str="", mode: Literal["stash", "exec"]="exec") -> tuple[boo
27
31
  mode: 运行模式,此参数为了同时执行多条命令提高效率
28
32
  * 使用 stash 仅会将命令流存到OSIS中,不会执行
29
33
  * 收到 exec 信号才会执行暂存包括当前的所有命令流。
34
+ timeout: HTTP 超时秒数,默认由 osis_client 决定
30
35
 
31
36
  Returns:
32
37
  tuple (bool, str): 是否成功,失败原因
33
38
  """
34
- response = osis_client("OSIS_Run", locals())
39
+ response = osis_client(
40
+ "OSIS_Run",
41
+ {"strCmd": strCmd, "mode": mode},
42
+ timeout=timeout,
43
+ )
35
44
  return response['success'], response['error']
36
45
 
37
46
  def _log(text, log_dir="pyosis_logs"):
@@ -69,7 +69,7 @@ class OSISEngine:
69
69
  - tendon: 钢束管理器(含 prop/shape 子管理器)
70
70
  - live: 活载管理器(含 grade/lane/case 子管理器)
71
71
  - geometry: 几何管理器(三维样条曲线)
72
- - property: 属性管理器(坐标系、收缩徐变、阻尼等)
72
+ - prop: 属性管理器(property 坐标系、收缩徐变、阻尼等)
73
73
  - thickness: 厚度管理器(壳厚度特性)
74
74
  - control: 控制管理器(全局参数、分析设置)
75
75
  - project: 项目管理器(创建、打开、保存项目)
@@ -246,9 +246,13 @@ class OSISEngine:
246
246
  if not ok:
247
247
  raise RuntimeError(f"执行失败: {err}")
248
248
 
249
- def solve(self) -> None:
250
- """求解工程"""
251
- ok, err = osis_solve()
249
+ def solve(self, timeout: float | None = None) -> None:
250
+ """求解工程
251
+
252
+ Args:
253
+ timeout: HTTP 超时秒数,默认 600(10 分钟)
254
+ """
255
+ ok, err = osis_solve(timeout=timeout)
252
256
  if not ok:
253
257
  raise RuntimeError(f"求解失败: {err}")
254
258
 
@@ -0,0 +1,32 @@
1
+ """
2
+ pyosis 自定义异常
3
+ """
4
+
5
+ class DependencyError(RuntimeError):
6
+ """删除实体时存在依赖项
7
+
8
+ Attributes:
9
+ entity_type: 实体类型字符串,如 "Material"
10
+ id_value: 实体编号(int)或名称(str)
11
+ no: 按编号寻址时的编号,否则为 None
12
+ name: 按名称寻址时的名称,否则为 None
13
+ dependencies: 依赖关系字典
14
+ """
15
+ def __init__(self, entity_type: str, id_value: int | str, dependencies: dict[str, list]):
16
+ self.entity_type = entity_type
17
+ self.id_value = id_value
18
+ self.dependencies = dependencies
19
+ if isinstance(id_value, int):
20
+ self.no = id_value
21
+ self.name = None
22
+ label = f"#{id_value}"
23
+ else:
24
+ self.no = None
25
+ self.name = id_value
26
+ label = id_value
27
+
28
+ lines = [f"无法删除 {entity_type} {label},存在以下依赖:"]
29
+ for kind, refs in dependencies.items():
30
+ if refs:
31
+ lines.append(f" - {kind}: {refs}")
32
+ super().__init__("\n".join(lines))
@@ -0,0 +1,87 @@
1
+ """
2
+ 通用引用查询客户端封装
3
+
4
+ 后端 C++ 暴露 /GetReferences 接口,根据 entityType 字符串分派
5
+ 到对应 Info 类的 getRelated* 方法。
6
+ """
7
+ from __future__ import annotations
8
+ from typing import Literal
9
+ from .client import osis_client
10
+ from .exceptions import DependencyError
11
+
12
+ # 支持的实体类型(与 C++ GetReferences.cpp 中的 if-else 分支对齐)
13
+ EntityType = Literal[
14
+ "Material", "Section", "Node", "Element", "Boundary",
15
+ "TendonShape", "TendonProp", "LoadCase", "LiveGrade", "LiveLane",
16
+ "CoorSys", "CreepShrink", "PUCurve", "ShellThickness",
17
+ "Damping", "Spline",
18
+ ]
19
+
20
+
21
+ """
22
+ 通用引用查询客户端封装
23
+ 后端 /GetReferences 统一字符串协议:{entityType, idKey("no"|"name"), idValue}
24
+ """
25
+
26
+
27
+
28
+ def get_references(entity_type: str, *, no: int | None = None, name: str | None = None) -> dict[str, list]:
29
+ """查询某个实体被哪些其他实体引用
30
+
31
+ Args:
32
+ entity_type: 实体类型,如 "Material" / "Node" / "TendonShape" / "LoadCase"
33
+ no: 按编号寻址时传(Material/Section/Node/Element)
34
+ name: 按名称寻址时传(TendonShape/LoadCase/...)
35
+ no 与 name 二选一。
36
+
37
+ Returns:
38
+ 引用关系字典,如 {"elements":[101,102], "loads":["自重"], ...}
39
+ 无依赖时返回空字典。
40
+
41
+ Raises:
42
+ ValueError: no/name 都没传,或都传了
43
+ RuntimeError: 查询失败
44
+ """
45
+ if no is not None and name is not None:
46
+ raise ValueError("no 与 name 只能传一个")
47
+ if no is None and name is None:
48
+ raise ValueError("必须提供 no 或 name")
49
+
50
+ if no is not None:
51
+ id_key, id_value = "no", str(no)
52
+ else:
53
+ id_key, id_value = "name", name
54
+
55
+ resp = osis_client("GetReferences", {
56
+ "entityType": entity_type,
57
+ "idKey": id_key,
58
+ "idValue": id_value,
59
+ })
60
+ if not resp.get("success"):
61
+ raise RuntimeError(f"查询 {entity_type}({id_key}={id_value}) 引用失败: {resp.get('error')}")
62
+ return resp.get("data") or {}
63
+
64
+
65
+ def raise_if_occupied(
66
+ entity_type: str,
67
+ deps: dict[str, list],
68
+ *,
69
+ no: int | None = None,
70
+ name: str | None = None,
71
+ ) -> None:
72
+ """存在非空依赖时抛出 DependencyError。
73
+
74
+ Args:
75
+ entity_type: 实体类型,如 "Material" / "TendonShape"
76
+ deps: get_references / get_dependencies 返回的依赖字典
77
+ no: 按编号寻址时传
78
+ name: 按名称寻址时传
79
+ """
80
+ non_empty = {k: v for k, v in deps.items() if v}
81
+ if not non_empty:
82
+ return
83
+ if no is None and name is None:
84
+ raise ValueError("必须提供 no 或 name")
85
+ id_value = no if no is not None else name
86
+ raise DependencyError(entity_type, id_value, non_empty)
87
+
@@ -579,7 +579,7 @@ class RspecAnalManager:
579
579
 
580
580
  def _load(self) -> list[RspecAnal]:
581
581
  """从服务端加载所有反应谱工况信息"""
582
- resp = osis_client("GetAllSeisRespSpecInfo", {})
582
+ resp = osis_client("GetAllRespSpecInfo", {})
583
583
  if not resp['success']:
584
584
  raise RuntimeError(f"{resp['error']}")
585
585
  rsp = [RspecAnal._from_dict(d) for d in resp.get("data", [])]
@@ -594,7 +594,7 @@ class RspecAnalManager:
594
594
  names = [str(name)]
595
595
  if not isinstance(names, list):
596
596
  raise TypeError(f"不支持的名称类型: {type(name)}")
597
- resp = osis_client("GetSeisRespSpecByNames", {"name": names})
597
+ resp = osis_client("GetRespSpecInfoByNames", {"name": names})
598
598
  if not resp['success']:
599
599
  raise RuntimeError(f"{resp['error']}")
600
600
  rsp = [RspecAnal._from_dict(d) if d else None for d in resp.get("data", [])]
@@ -15,6 +15,7 @@ from dataclasses import dataclass, field
15
15
  from typing import Any, Literal
16
16
  from enum import Enum
17
17
  from ..core.client import osis_client
18
+ from ..core import get_references, raise_if_occupied
18
19
  from .interface import (
19
20
  osis_element_beam3d,
20
21
  osis_element_truss,
@@ -555,6 +556,10 @@ class ElementManager:
555
556
  ele_no = [ele.no for ele in elements]
556
557
  return max(ele_no) + 1
557
558
 
559
+ def get_dependencies(self, no: int) -> dict[str, list]:
560
+ """查询单元被谁引用"""
561
+ return get_references("Element", no=no)
562
+
558
563
  # ── 增删改 ────────────────────────────────
559
564
  def create(self, no: int | None, type: str, *args: Any, **kwargs: Any) -> Element:
560
565
  """创建单元(便捷入口,内部转发到对应 create_* 方法)
@@ -699,7 +704,14 @@ class ElementManager:
699
704
  return self.get(no)
700
705
 
701
706
  def delete(self, no: int) -> None:
702
- """删除单元"""
707
+ """删除单元
708
+
709
+ Raises:
710
+ DependencyError: 存在依赖项时
711
+ RuntimeError: 删除失败时抛出异常
712
+ """
713
+ deps = self.get_dependencies(no)
714
+ raise_if_occupied("Element", deps, no=no)
703
715
  ok, err = osis_element_del(no)
704
716
  if not ok:
705
717
  raise RuntimeError(f"删除单元 {no} 失败: {err}")
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Dict, Literal, List, Union, Sequence
2
2
  from ..core import REGISTRY, osis_run
3
- from ..core.client import osis_client
3
+ from ..core.client import DEFAULT_SOLVE_TIMEOUT, osis_client
4
4
 
5
5
  def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
6
6
  """
@@ -11,7 +11,7 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
11
11
  matrix_data: 任意维度的列表(1维/2维/3维/...)或单个数值(0维)
12
12
 
13
13
  Returns:
14
- OSIS DSL格式的字符串(拼接后的完整命令)
14
+ tuple (bool, str): 是否成功,失败原因
15
15
 
16
16
  Example:
17
17
  >>> # 2维矩阵示例
@@ -92,14 +92,10 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
92
92
  new_indices = indices + [idx]
93
93
  generate_assignments(elem, new_indices)
94
94
  else:
95
- # 处理数值赋值
96
95
  value = data
97
-
98
- # 格式化数值
99
- if isinstance(value, int) or isinstance(value, float):
96
+ if isinstance(value, (int, float)):
100
97
  val_str = str(value)
101
98
  else:
102
- # 其他类型尝试转为string
103
99
  val_str = f"\"{value}\""
104
100
  idx_parts = list(indices)
105
101
  if not is_charn:
@@ -107,17 +103,11 @@ def osis_matrix(matrix_name: str, matrix_data: Union[List, int, float, str]):
107
103
  idx_parts.append(0)
108
104
  idx_str = ",".join(map(str, idx_parts))
109
105
  assign_cmd = f"{matrix_name}[{idx_str}] = {val_str}"
110
- # _log(assign_cmd)
111
- # osis_run(assign_cmd, "stash")
112
106
  all_cmds.append(assign_cmd)
113
-
114
- # 开始递归生成赋值语句(OSIS索引从0开始)
107
+
115
108
  generate_assignments(matrix_data, [])
116
- # osis_run()
117
- # 返回拼接后的完整命令字符串
118
- # str_cmds = "\n".join(all_cmds)
109
+
119
110
  str_cmds = ";".join(all_cmds)
120
- # _log(str_cmds)
121
111
  return osis_run(str_cmds, "exec")
122
112
 
123
113
  def output_result_for_calc_book():
@@ -154,12 +144,18 @@ def osis_clc():
154
144
  """
155
145
  pass
156
146
 
157
- @REGISTRY.register("Solve")
158
- def osis_solve():
147
+ def osis_solve(timeout: float | None = None):
159
148
  """
160
149
  求解工程
161
-
150
+
151
+ Args:
152
+ timeout: HTTP 超时秒数,默认 600(10 分钟)
153
+
162
154
  Returns:
163
155
  tuple (bool, str): 是否成功,失败原因
164
156
  """
165
- pass
157
+ return osis_run(
158
+ "Solve;",
159
+ "exec",
160
+ timeout=timeout if timeout is not None else DEFAULT_SOLVE_TIMEOUT,
161
+ )
@@ -19,6 +19,7 @@ from enum import Enum
19
19
  from typing import Literal, Any
20
20
 
21
21
  from ..core.client import osis_client
22
+ from ..core import get_references, raise_if_occupied
22
23
  from .interface import (
23
24
  osis_spline3d_general,
24
25
  osis_spline3d_natural,
@@ -286,6 +287,10 @@ class GeometryManager:
286
287
  raise RuntimeError(f"创建3D圆弧 {name} 失败: {err}")
287
288
  return self.get(name)
288
289
 
290
+ def get_dependencies(self, name: str) -> dict[str, list]:
291
+ """查询几何样条被谁引用"""
292
+ return get_references("Spline", name=name)
293
+
289
294
  def delete(self, name: str) -> None:
290
295
  """删除三维样条曲线
291
296
 
@@ -293,8 +298,11 @@ class GeometryManager:
293
298
  name: 曲线名称
294
299
 
295
300
  Raises:
301
+ DependencyError: 存在依赖项时
296
302
  RuntimeError: 删除失败时抛出异常
297
303
  """
304
+ deps = self.get_dependencies(name)
305
+ raise_if_occupied("Spline", deps, name=name)
298
306
  ok, err = osis_spline3d_del(name)
299
307
  if not ok:
300
308
  raise RuntimeError(f"删除样条曲线 {name} 失败: {err}")
@@ -17,6 +17,7 @@ from dataclasses import dataclass, field
17
17
  from typing import Literal, Any
18
18
 
19
19
  from ..core.client import osis_client
20
+ from ..core import get_references, raise_if_occupied
20
21
  from .grade import (
21
22
  osis_livegrade_highway,
22
23
  osis_livegrade_vehicle,
@@ -499,6 +500,10 @@ class LiveGradeManager:
499
500
  ]
500
501
  return grades
501
502
 
503
+ def get_dependencies(self, name: str) -> dict[str, list]:
504
+ """查询活载等级被谁引用"""
505
+ return get_references("LiveGrade", name=name)
506
+
502
507
  def create(
503
508
  self,
504
509
  name: str,
@@ -713,8 +718,11 @@ class LiveGradeManager:
713
718
  name: 活载等级名称
714
719
 
715
720
  Raises:
721
+ DependencyError: 存在依赖项时
716
722
  RuntimeError: 删除失败时抛出异常
717
723
  """
724
+ deps = self.get_dependencies(name)
725
+ raise_if_occupied("LiveGrade", deps, name=name)
718
726
  ok, err = osis_livegrade_del(name)
719
727
  if not ok:
720
728
  raise RuntimeError(f"删除活载等级 {name} 失败: {err}")
@@ -815,6 +823,10 @@ class LaneManager:
815
823
  ]
816
824
  return lanes
817
825
 
826
+ def get_dependencies(self, name: str) -> dict[str, list]:
827
+ """查询车道被谁引用"""
828
+ return get_references("LiveLane", name=name)
829
+
818
830
  def create(
819
831
  self,
820
832
  name: str,
@@ -966,8 +978,11 @@ class LaneManager:
966
978
  name: 车道名称
967
979
 
968
980
  Raises:
981
+ DependencyError: 存在依赖项时
969
982
  RuntimeError: 删除失败时抛出异常
970
983
  """
984
+ deps = self.get_dependencies(name)
985
+ raise_if_occupied("LiveLane", deps, name=name)
971
986
  ok, err = osis_lane_del(name)
972
987
  if not ok:
973
988
  raise RuntimeError(f"删除车道 {name} 失败: {err}")
@@ -17,6 +17,7 @@ from typing import Literal, Any
17
17
  from enum import Enum
18
18
 
19
19
  from ..core.client import osis_client
20
+ from ..core import get_references, raise_if_occupied
20
21
  from .loadcase import (
21
22
  osis_loadcase,
22
23
  osis_loadcase_del,
@@ -38,6 +39,7 @@ from .tendon import (
38
39
  osis_tendon_shape_mod,
39
40
  osis_layout_tendons,
40
41
  osis_wipe_tendons,
42
+ osis_bottom_tendons,
41
43
  )
42
44
  from .static import (
43
45
  osis_load_gravity,
@@ -860,6 +862,17 @@ class TendonShape:
860
862
  if not ok:
861
863
  raise RuntimeError(f"擦除钢束 {self.name} 失败: {err}")
862
864
 
865
+ def bottom(self, b_bot: int, *bot: int) -> None:
866
+ """定义是否沿梁单元底板布置钢束形状
867
+
868
+ Args:
869
+ b_bot: 0=不沿底板布置,1=沿底板布置
870
+ *bot: 底板布置对应的样条曲线行号,数量不限(Bot_i, Bot_j, ...)
871
+ """
872
+ ok, err = osis_bottom_tendons(self.name, b_bot, *bot)
873
+ if not ok:
874
+ raise RuntimeError(f"设置钢束 {self.name} 底板布置失败: {err}")
875
+
863
876
  def rename(self, new_name: str) -> None:
864
877
  """重命名钢束形状
865
878
 
@@ -897,6 +910,10 @@ class TendonPropManager:
897
910
  ]
898
911
  return props
899
912
 
913
+ def get_dependencies(self, name: str) -> dict[str, list]:
914
+ """查询钢束特性被谁引用"""
915
+ return get_references("TendonProp", name=name)
916
+
900
917
  def create(
901
918
  self,
902
919
  name: str,
@@ -1207,8 +1224,11 @@ class TendonPropManager:
1207
1224
  name: 钢束特性名称
1208
1225
 
1209
1226
  Raises:
1227
+ DependencyError: 存在依赖项时
1210
1228
  RuntimeError: 删除失败时抛出异常
1211
1229
  """
1230
+ deps = self.get_dependencies(name)
1231
+ raise_if_occupied("TendonProp", deps, name=name)
1212
1232
  ok, err = osis_tendon_prop_del(name)
1213
1233
  if not ok:
1214
1234
  raise RuntimeError(f"删除钢束特性 {name} 失败: {err}")
@@ -1308,6 +1328,10 @@ class TendonShapeManager:
1308
1328
  ]
1309
1329
  return shapes
1310
1330
 
1331
+ def get_dependencies(self, name: str) -> dict[str, list]:
1332
+ """查询钢束形状被谁引用"""
1333
+ return get_references("TendonShape", name=name)
1334
+
1311
1335
  def create(
1312
1336
  self,
1313
1337
  name: str,
@@ -1463,8 +1487,11 @@ class TendonShapeManager:
1463
1487
  name: 钢束形状名称
1464
1488
 
1465
1489
  Raises:
1490
+ DependencyError: 存在依赖项时
1466
1491
  RuntimeError: 删除失败时抛出异常
1467
1492
  """
1493
+ deps = self.get_dependencies(name)
1494
+ raise_if_occupied("TendonShape", deps, name=name)
1468
1495
  ok, err = osis_tendon_shape_del(name)
1469
1496
  if not ok:
1470
1497
  raise RuntimeError(f"删除钢束形状 {name} 失败: {err}")
@@ -1628,6 +1655,10 @@ class LoadCaseManager:
1628
1655
  ]
1629
1656
  return loadcases
1630
1657
 
1658
+ def get_dependencies(self, name: str) -> dict[str, list]:
1659
+ """查询荷载工况被谁引用"""
1660
+ return get_references("LoadCase", name=name)
1661
+
1631
1662
  # ── 增删改 ────────────────────────────────
1632
1663
 
1633
1664
  def create(
@@ -1690,8 +1721,11 @@ class LoadCaseManager:
1690
1721
  name: 荷载工况名称
1691
1722
 
1692
1723
  Raises:
1724
+ DependencyError: 存在依赖项时
1693
1725
  RuntimeError: 删除失败时抛出异常
1694
1726
  """
1727
+ deps = self.get_dependencies(name)
1728
+ raise_if_occupied("LoadCase", deps, name=name)
1695
1729
  ok, err = osis_loadcase_del(name)
1696
1730
  if not ok:
1697
1731
  raise RuntimeError(f"删除荷载工况 {name} 失败: {err}")
@@ -328,3 +328,21 @@ def osis_wipe_tendons(strShapeName: str):
328
328
  tuple (bool, str): 是否成功,失败原因
329
329
  '''
330
330
  pass
331
+
332
+ @REGISTRY.register("BottomTS")
333
+ def osis_bottom_tendons(TdShapeName: str, bBot: int, *Bot: int):
334
+ '''
335
+ 定义是否沿梁单元底板布置钢束形状
336
+
337
+ Args:
338
+ TdShapeName (str): 钢束形状名称
339
+ bBot (int): 是否沿底板布置
340
+ * 0 = 不沿底板布置
341
+ * 1 = 沿底板布置
342
+ *Bot (int): 底板布置钢束对应的样条曲线行号,数量不限
343
+ (如 Bot_i, Bot_j, Bot_k, ...)
344
+
345
+ Returns:
346
+ tuple (bool, str): 是否成功,失败原因
347
+ '''
348
+ pass
@@ -23,7 +23,7 @@ from .interface import (
23
23
  osis_material_del,
24
24
  osis_material_mod,
25
25
  )
26
-
26
+ from ..core import get_references, raise_if_occupied
27
27
 
28
28
  # ──────────────────────────────────────────────
29
29
  # 数据类
@@ -118,6 +118,10 @@ class MaterialManager:
118
118
 
119
119
  return materials
120
120
 
121
+ def get_dependencies(self, no: int) -> dict[str, list]:
122
+ """查询材料被谁引用"""
123
+ return get_references("Material", no=no)
124
+
121
125
  def _next_no(self) -> int:
122
126
  """生成下一个可用材料编号
123
127
 
@@ -342,8 +346,11 @@ class MaterialManager:
342
346
  no: 材料编号
343
347
 
344
348
  Raises:
349
+ DependencyError: 存在依赖项时(force=False 才会抛)
345
350
  RuntimeError: 删除失败时抛出异常
346
351
  """
352
+ deps = self.get_dependencies(no)
353
+ raise_if_occupied("Material", deps, no=no)
347
354
  ok, err = osis_material_del(no)
348
355
  if not ok:
349
356
  raise RuntimeError(f"删除材料 {no} 失败: {err}")