dimine-python-sdk 0.2.2__py3-none-any.whl → 0.2.4__py3-none-any.whl
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.
- dimine_python_sdk/__init__.py +24 -4
- dimine_python_sdk/cli/commands/camera.py +3 -4
- dimine_python_sdk/cli/commands/entity.py +5 -6
- dimine_python_sdk/cli/commands/feature.py +5 -6
- dimine_python_sdk/cli/commands/file.py +31 -24
- dimine_python_sdk/cli/commands/geometry.py +9 -9
- dimine_python_sdk/cli/commands/laneway.py +3 -3
- dimine_python_sdk/cli/commands/layer.py +5 -6
- dimine_python_sdk/cli/commands/text.py +4 -4
- dimine_python_sdk/cli/utils/_utils.py +70 -28
- dimine_python_sdk/conn/base_conn_client.py +5 -1
- dimine_python_sdk/conn/conn_client.py +2 -2
- dimine_python_sdk/docs/guides/lib/io/SUMMARY.md +1 -0
- dimine_python_sdk/docs/guides/lib/io/dmo_file.md +159 -0
- dimine_python_sdk/docs/reference/SUMMARY.md +1 -0
- dimine_python_sdk/docs/reference/lib/io/dmo_file.md +3 -0
- dimine_python_sdk/lib/__init__.py +19 -4
- dimine_python_sdk/lib/io/__init__.py +19 -0
- dimine_python_sdk/lib/io/dmo_file.py +327 -0
- dimine_python_sdk/lib/io/file_converter.py +3 -3
- dimine_python_sdk/lib/native/__init__.py +4 -0
- dimine_python_sdk/lib/native/io.py +22 -10
- dimine_python_sdk/models/types.py +9 -9
- dimine_python_sdk/site/404.html +28 -0
- dimine_python_sdk/site/examples/cli_usage.html +28 -0
- dimine_python_sdk/site/examples/contour_modeling_visualization.html +28 -0
- dimine_python_sdk/site/examples/import_csv_to_drill_db.html +28 -0
- dimine_python_sdk/site/examples/lib_file_usage.html +28 -0
- dimine_python_sdk/site/examples/reserve_estimation_pipeline.html +28 -0
- dimine_python_sdk/site/examples/summary.html +28 -0
- dimine_python_sdk/site/guides/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/cli.html +28 -0
- dimine_python_sdk/site/guides/conn/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/conn/data_edit.html +28 -0
- dimine_python_sdk/site/guides/conn/data_get.html +28 -0
- dimine_python_sdk/site/guides/conn/features.html +28 -0
- dimine_python_sdk/site/guides/conn/io.html +28 -0
- dimine_python_sdk/site/guides/lib/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/lib/exploitation/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/lib/exploitation/contour_modeling.html +28 -0
- dimine_python_sdk/site/guides/lib/exploitation/implicit_modeling.html +28 -0
- dimine_python_sdk/site/guides/lib/exploitation/laneway.html +28 -0
- dimine_python_sdk/site/guides/lib/io/SUMMARY.html +29 -0
- dimine_python_sdk/site/guides/lib/io/dmb_file.html +28 -0
- dimine_python_sdk/site/guides/lib/io/dmd_file.html +28 -0
- dimine_python_sdk/site/guides/lib/io/dmf_file.html +28 -0
- dimine_python_sdk/site/guides/lib/io/dmg_file.html +29 -1
- dimine_python_sdk/site/guides/lib/io/dmo_file.html +2335 -0
- dimine_python_sdk/site/guides/lib/io/dmt_file.html +29 -1
- dimine_python_sdk/site/guides/lib/io/file_conversion.html +28 -0
- dimine_python_sdk/site/guides/lib/prospecting/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/lib/prospecting/drill_db.html +28 -0
- dimine_python_sdk/site/guides/lib/prospecting/reserve_estimation.html +28 -0
- dimine_python_sdk/site/guides/lib/visualization/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/lib/visualization/visualization.html +28 -0
- dimine_python_sdk/site/guides/models/SUMMARY.html +28 -0
- dimine_python_sdk/site/guides/models/data_types.html +28 -0
- dimine_python_sdk/site/index.html +28 -0
- dimine_python_sdk/site/objects.inv +0 -0
- dimine_python_sdk/site/quickstart.html +28 -0
- dimine_python_sdk/site/reference/SUMMARY.html +29 -0
- dimine_python_sdk/site/reference/cli/commands/camera.html +39 -11
- dimine_python_sdk/site/reference/cli/commands/convert.html +28 -0
- dimine_python_sdk/site/reference/cli/commands/datatable.html +28 -0
- dimine_python_sdk/site/reference/cli/commands/entity.html +46 -18
- dimine_python_sdk/site/reference/cli/commands/feature.html +47 -19
- dimine_python_sdk/site/reference/cli/commands/file.html +115 -69
- dimine_python_sdk/site/reference/cli/commands/geometry.html +39 -11
- dimine_python_sdk/site/reference/cli/commands/laneway.html +30 -2
- dimine_python_sdk/site/reference/cli/commands/layer.html +47 -19
- dimine_python_sdk/site/reference/cli/commands/text.html +34 -2
- dimine_python_sdk/site/reference/conn/base_conn_client.html +50 -14
- dimine_python_sdk/site/reference/conn/conn_client.html +28 -0
- dimine_python_sdk/site/reference/lib/bridge.html +132 -82
- dimine_python_sdk/site/reference/lib/exploitation/api.html +28 -0
- dimine_python_sdk/site/reference/lib/exploitation/contour_modeling.html +28 -0
- dimine_python_sdk/site/reference/lib/exploitation/implicit_modeling.html +28 -0
- dimine_python_sdk/site/reference/lib/exploitation/lane_way.html +28 -0
- dimine_python_sdk/site/reference/lib/exploitation/models.html +28 -0
- dimine_python_sdk/site/reference/lib/io/dmb_file.html +28 -0
- dimine_python_sdk/site/reference/lib/io/dmd_file.html +28 -0
- dimine_python_sdk/site/reference/lib/io/dmf_file.html +28 -0
- dimine_python_sdk/site/reference/lib/io/dmg_file.html +28 -0
- dimine_python_sdk/site/reference/lib/io/dmo_file.html +3003 -0
- dimine_python_sdk/site/reference/lib/io/dmt_file.html +28 -0
- dimine_python_sdk/site/reference/lib/io/file_converter.html +32 -4
- dimine_python_sdk/site/reference/lib/io/models.html +28 -0
- dimine_python_sdk/site/reference/lib/native/algorithm.html +28 -0
- dimine_python_sdk/site/reference/lib/native/data_table.html +28 -0
- dimine_python_sdk/site/reference/lib/native/exploitation.html +28 -0
- dimine_python_sdk/site/reference/lib/native/geometry.html +28 -0
- dimine_python_sdk/site/reference/lib/native/io.html +132 -82
- dimine_python_sdk/site/reference/lib/native/models.html +28 -0
- dimine_python_sdk/site/reference/lib/native/prospecting.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/api.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/block_model.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/compositing.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/drill_db.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/ellipsoid.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/high_grade.html +28 -0
- dimine_python_sdk/site/reference/lib/prospecting/models.html +28 -0
- dimine_python_sdk/site/reference/lib/visualization/exceptions.html +28 -0
- dimine_python_sdk/site/reference/lib/visualization/models.html +28 -0
- dimine_python_sdk/site/reference/lib/visualization/tin_renderer.html +28 -0
- dimine_python_sdk/site/reference/logger.html +28 -0
- dimine_python_sdk/site/reference/models/conn.html +28 -0
- dimine_python_sdk/site/reference/models/errors.html +28 -0
- dimine_python_sdk/site/reference/models/types.html +36 -8
- dimine_python_sdk/site/search/search_index.json +1 -1
- dimine_python_sdk/site/sitemap.xml +90 -82
- dimine_python_sdk/site/sitemap.xml.gz +0 -0
- dimine_python_sdk/site/summary.html +28 -0
- {dimine_python_sdk-0.2.2.dist-info → dimine_python_sdk-0.2.4.dist-info}/METADATA +1 -1
- {dimine_python_sdk-0.2.2.dist-info → dimine_python_sdk-0.2.4.dist-info}/RECORD +117 -147
- {dimine_python_sdk-0.2.2.dist-info → dimine_python_sdk-0.2.4.dist-info}/top_level.txt +0 -2
- outline/build_dmd_from_scratch.py +0 -426
- outline/contour_extract.py +0 -450
- outline/contour_match.py +0 -1095
- outline/contour_pipeline.py +0 -586
- outline/contour_validate.py +0 -645
- outline/dmd_to_dmg.py +0 -164
- outline/drill_to_reserves.py +0 -825
- outline/ellipsoid_params.py +0 -106
- outline/implicit_modeling.py +0 -365
- outline/read_dmb.py +0 -200
- outline/read_dwg.py +0 -353
- outline/test_dmd_file.py +0 -322
- outline/test_dmg_to_segments.py +0 -317
- outline/test_extend_table.py +0 -193
- tests/conn_example.py +0 -70
- tests/conn_test.py +0 -269
- tests/test_conn_client_unit.py +0 -449
- tests/test_create_geometry_file_transfer.py +0 -289
- tests/test_csv_import_flow.py +0 -169
- tests/test_dmg_file.py +0 -467
- tests/test_dmt_file.py +0 -178
- tests/test_file_conversion.py +0 -544
- tests/test_high_grade_samples.py +0 -228
- tests/test_implicit_modeling.py +0 -284
- tests/test_laneway.py +0 -341
- tests/test_layer_isolation.py +0 -219
- tests/test_lib_file.py +0 -731
- tests/test_lib_refactor.py +0 -304
- tests/test_modeling.py +0 -338
- tests/test_native_unit.py +0 -353
- tests/test_prospecting_api.py +0 -1287
- tests/test_sample_compositing.py +0 -398
- tests/test_tin_renderer.py +0 -454
- tests/ws_client.py +0 -60
- tests/ws_server.py +0 -271
- {dimine_python_sdk-0.2.2.dist-info → dimine_python_sdk-0.2.4.dist-info}/WHEEL +0 -0
- {dimine_python_sdk-0.2.2.dist-info → dimine_python_sdk-0.2.4.dist-info}/entry_points.txt +0 -0
dimine_python_sdk/__init__.py
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
4
5
|
|
|
5
6
|
DOCS_PATH = Path(__file__).parent / "docs"
|
|
6
|
-
__VERSION__ = "0.2.
|
|
7
|
+
__VERSION__ = "0.2.4" # 由 build.bat 中的 generate_version.py 自动更新
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
_LAZY_IMPORTS: dict[str, Any] = {}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def __getattr__(name: str) -> Any:
|
|
14
|
+
"""PEP 562: 惰性加载 ConnClient,避免导入期触发 numpy/pydantic/websockets 等重依赖。"""
|
|
15
|
+
if name == "ConnClient":
|
|
16
|
+
if name not in _LAZY_IMPORTS:
|
|
17
|
+
from dimine_python_sdk.conn import ConnClient as _ConnClient
|
|
18
|
+
|
|
19
|
+
_LAZY_IMPORTS[name] = _ConnClient
|
|
20
|
+
return _LAZY_IMPORTS[name]
|
|
21
|
+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def __dir__() -> list[str]:
|
|
25
|
+
"""确保 dir() 和 IDE 自动补全能看到惰性加载的属性。"""
|
|
26
|
+
return sorted(set(list(globals()) + __all__ + list(_LAZY_IMPORTS)))
|
|
7
27
|
|
|
8
28
|
|
|
9
29
|
__all__ = [
|
|
10
30
|
"ConnClient",
|
|
11
31
|
"DOCS_PATH",
|
|
12
|
-
"__VERSION__"
|
|
32
|
+
"__VERSION__",
|
|
13
33
|
]
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""相机与截图相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
get_client,
|
|
7
5
|
handle_sdk_error,
|
|
@@ -9,6 +7,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
9
7
|
output_result,
|
|
10
8
|
parse_json_arg,
|
|
11
9
|
require_remote_sdk,
|
|
10
|
+
run_cli_async,
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
|
|
@@ -35,7 +34,7 @@ def cmd_camera_set(fmt: str, center_point: str, normal_vector: str) -> None:
|
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
try:
|
|
38
|
-
result =
|
|
37
|
+
result = run_cli_async(_run())
|
|
39
38
|
output_result(result, fmt)
|
|
40
39
|
except Exception as e:
|
|
41
40
|
handle_sdk_error(e, fmt, "设置相机视角失败")
|
|
@@ -56,7 +55,7 @@ def cmd_camera_screenshot(fmt: str) -> None:
|
|
|
56
55
|
}
|
|
57
56
|
|
|
58
57
|
try:
|
|
59
|
-
result =
|
|
58
|
+
result = run_cli_async(_run())
|
|
60
59
|
output_result(result, fmt)
|
|
61
60
|
except Exception as e:
|
|
62
61
|
handle_sdk_error(e, fmt, "截图失败")
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""实体相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
ENTITY_TYPE_CN,
|
|
7
5
|
fmt_selected_item,
|
|
@@ -11,6 +9,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
11
9
|
output_result,
|
|
12
10
|
parse_entity_ids,
|
|
13
11
|
require_remote_sdk,
|
|
12
|
+
run_cli_async,
|
|
14
13
|
safe_int,
|
|
15
14
|
)
|
|
16
15
|
|
|
@@ -57,7 +56,7 @@ def cmd_get_selected(fmt: str, page_size: int, page: int) -> None:
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
try:
|
|
60
|
-
result =
|
|
59
|
+
result = run_cli_async(_run())
|
|
61
60
|
output_result(result, fmt)
|
|
62
61
|
except Exception as e:
|
|
63
62
|
handle_sdk_error(e, fmt, "获取选中实体失败")
|
|
@@ -100,7 +99,7 @@ def cmd_select(fmt: str, file_id: str | None, ids: str | None, mode: str) -> Non
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
try:
|
|
103
|
-
result =
|
|
102
|
+
result = run_cli_async(_run())
|
|
104
103
|
output_result(result, fmt)
|
|
105
104
|
except Exception as e:
|
|
106
105
|
handle_sdk_error(e, fmt, "选中操作失败")
|
|
@@ -127,7 +126,7 @@ def cmd_delete(fmt: str, file_id: str | None, ids: str) -> None:
|
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
try:
|
|
130
|
-
result =
|
|
129
|
+
result = run_cli_async(_run())
|
|
131
130
|
output_result(result, fmt)
|
|
132
131
|
except Exception as e:
|
|
133
132
|
handle_sdk_error(e, fmt, "删除实体失败")
|
|
@@ -162,7 +161,7 @@ def _set_visibility(fmt: str, file_id: str | None, raw_ids: str, action: str) ->
|
|
|
162
161
|
}
|
|
163
162
|
|
|
164
163
|
try:
|
|
165
|
-
result =
|
|
164
|
+
result = run_cli_async(_run())
|
|
166
165
|
output_result(result, fmt)
|
|
167
166
|
except Exception as e:
|
|
168
167
|
handle_sdk_error(e, fmt, "设置可见性失败")
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""要素/特征定义相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
get_client,
|
|
7
5
|
handle_sdk_error,
|
|
@@ -10,6 +8,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
10
8
|
parse_fields_arg,
|
|
11
9
|
raise_or_exit,
|
|
12
10
|
require_remote_sdk,
|
|
11
|
+
run_cli_async,
|
|
13
12
|
)
|
|
14
13
|
|
|
15
14
|
|
|
@@ -38,7 +37,7 @@ def cmd_feature_list(fmt: str, file_id: str | None) -> None:
|
|
|
38
37
|
return {"file_id": file_id, "feature_count": len(result), "features": result}
|
|
39
38
|
|
|
40
39
|
try:
|
|
41
|
-
result =
|
|
40
|
+
result = run_cli_async(_run())
|
|
42
41
|
output_result(result, fmt)
|
|
43
42
|
except SystemExit:
|
|
44
43
|
raise
|
|
@@ -70,7 +69,7 @@ def cmd_feature_create(fmt: str, file_id: str, name: str, fields: str) -> None:
|
|
|
70
69
|
}
|
|
71
70
|
|
|
72
71
|
try:
|
|
73
|
-
result =
|
|
72
|
+
result = run_cli_async(_run())
|
|
74
73
|
output_result(result, fmt)
|
|
75
74
|
except Exception as e:
|
|
76
75
|
handle_sdk_error(e, fmt, "创建要素失败")
|
|
@@ -100,7 +99,7 @@ def cmd_feature_update(fmt: str, file_id: str, name: str, fields: str) -> None:
|
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
try:
|
|
103
|
-
result =
|
|
102
|
+
result = run_cli_async(_run())
|
|
104
103
|
output_result(result, fmt)
|
|
105
104
|
except Exception as e:
|
|
106
105
|
handle_sdk_error(e, fmt, "更新要素失败")
|
|
@@ -123,7 +122,7 @@ def cmd_feature_delete(fmt: str, file_id: str, name: str) -> None:
|
|
|
123
122
|
}
|
|
124
123
|
|
|
125
124
|
try:
|
|
126
|
-
result =
|
|
125
|
+
result = run_cli_async(_run())
|
|
127
126
|
output_result(result, fmt)
|
|
128
127
|
except Exception as e:
|
|
129
128
|
handle_sdk_error(e, fmt, "删除要素失败")
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"""文件相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
get_client,
|
|
7
5
|
handle_sdk_error,
|
|
8
|
-
open_client,
|
|
9
6
|
output_error,
|
|
10
7
|
output_result,
|
|
11
8
|
raise_or_exit,
|
|
12
9
|
require_remote_sdk,
|
|
13
10
|
resolve_path,
|
|
11
|
+
run_cli_async,
|
|
14
12
|
)
|
|
15
13
|
|
|
16
14
|
|
|
@@ -36,7 +34,7 @@ def cmd_file_tree(fmt: str) -> None:
|
|
|
36
34
|
return {"file_count": len(result), "files": result}
|
|
37
35
|
|
|
38
36
|
try:
|
|
39
|
-
result =
|
|
37
|
+
result = run_cli_async(_run())
|
|
40
38
|
output_result(result, fmt)
|
|
41
39
|
except Exception as e:
|
|
42
40
|
handle_sdk_error(e, fmt, "获取文件树失败")
|
|
@@ -52,21 +50,20 @@ def cmd_file_save(fmt: str, output_path: str, file_id: str | None) -> None:
|
|
|
52
50
|
|
|
53
51
|
async def _run():
|
|
54
52
|
nonlocal file_id
|
|
53
|
+
client = await get_client()
|
|
55
54
|
if not file_id:
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
async with open_client() as client:
|
|
69
|
-
await client.export_file(file_id=file_id, file_path=save_path)
|
|
55
|
+
files = await client.get_files()
|
|
56
|
+
if not files:
|
|
57
|
+
raise_or_exit("没有打开的文件。请先在 Dimine 中打开文件,或提供 --file-id 参数。", fmt)
|
|
58
|
+
if len(files) == 1:
|
|
59
|
+
file_id = files[0].id
|
|
60
|
+
else:
|
|
61
|
+
file_list = "\n".join([f" - id: {f.id}, name: {f.name}" for f in files])
|
|
62
|
+
raise_or_exit(
|
|
63
|
+
f"当前打开了 {len(files)} 个文件,请指定 --file-id:\n{file_list}",
|
|
64
|
+
fmt,
|
|
65
|
+
)
|
|
66
|
+
await client.export_file(file_id=file_id, file_path=save_path)
|
|
70
67
|
return {
|
|
71
68
|
"success": True,
|
|
72
69
|
"file_id": file_id,
|
|
@@ -75,7 +72,7 @@ def cmd_file_save(fmt: str, output_path: str, file_id: str | None) -> None:
|
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
try:
|
|
78
|
-
result =
|
|
75
|
+
result = run_cli_async(_run())
|
|
79
76
|
output_result(result, fmt)
|
|
80
77
|
except SystemExit:
|
|
81
78
|
raise
|
|
@@ -84,7 +81,7 @@ def cmd_file_save(fmt: str, output_path: str, file_id: str | None) -> None:
|
|
|
84
81
|
|
|
85
82
|
|
|
86
83
|
def cmd_file_open(fmt: str, path: str) -> None:
|
|
87
|
-
"""在 Dimine 中打开文件(需 Dimine
|
|
84
|
+
"""在 Dimine 中打开文件(需 Dimine 运行)。如果文件已在 Dimine 中打开,先关闭再重新打开。"""
|
|
88
85
|
err = require_remote_sdk()
|
|
89
86
|
if err:
|
|
90
87
|
output_error(err, fmt)
|
|
@@ -93,6 +90,16 @@ def cmd_file_open(fmt: str, path: str) -> None:
|
|
|
93
90
|
|
|
94
91
|
async def _run():
|
|
95
92
|
client = await get_client()
|
|
93
|
+
|
|
94
|
+
# 检查是否有同名文件已打开,如果有则先关闭
|
|
95
|
+
target_name = path
|
|
96
|
+
opened_files = await client.get_files()
|
|
97
|
+
for f in opened_files:
|
|
98
|
+
if f.name == target_name:
|
|
99
|
+
await client.close_file(f.id)
|
|
100
|
+
break
|
|
101
|
+
|
|
102
|
+
# 打开文件
|
|
96
103
|
await client.open_file(file_path)
|
|
97
104
|
return {
|
|
98
105
|
"success": True,
|
|
@@ -101,7 +108,7 @@ def cmd_file_open(fmt: str, path: str) -> None:
|
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
try:
|
|
104
|
-
result =
|
|
111
|
+
result = run_cli_async(_run())
|
|
105
112
|
output_result(result, fmt)
|
|
106
113
|
except Exception as e:
|
|
107
114
|
handle_sdk_error(e, fmt, "打开文件失败")
|
|
@@ -123,7 +130,7 @@ def cmd_file_close(fmt: str, file_id: str) -> None:
|
|
|
123
130
|
}
|
|
124
131
|
|
|
125
132
|
try:
|
|
126
|
-
result =
|
|
133
|
+
result = run_cli_async(_run())
|
|
127
134
|
output_result(result, fmt)
|
|
128
135
|
except Exception as e:
|
|
129
136
|
handle_sdk_error(e, fmt, "关闭文件失败")
|
|
@@ -147,7 +154,7 @@ def cmd_file_create(fmt: str, path: str) -> None:
|
|
|
147
154
|
}
|
|
148
155
|
|
|
149
156
|
try:
|
|
150
|
-
result =
|
|
157
|
+
result = run_cli_async(_run())
|
|
151
158
|
output_result(result, fmt)
|
|
152
159
|
except Exception as e:
|
|
153
160
|
handle_sdk_error(e, fmt, "创建文件失败")
|
|
@@ -180,7 +187,7 @@ def cmd_file_visibility(fmt: str, file_id: str, action: str) -> None:
|
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
try:
|
|
183
|
-
result =
|
|
190
|
+
result = run_cli_async(_run())
|
|
184
191
|
output_result(result, fmt)
|
|
185
192
|
except Exception as e:
|
|
186
193
|
handle_sdk_error(e, fmt, "设置文件可见性失败")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"""几何体相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
3
|
import sys
|
|
5
4
|
|
|
6
5
|
from dimine_python_sdk.cli.utils._utils import (
|
|
@@ -13,6 +12,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
13
12
|
parse_color,
|
|
14
13
|
output_result,
|
|
15
14
|
require_remote_sdk,
|
|
15
|
+
run_cli_async,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
|
|
@@ -308,9 +308,9 @@ def _build_partial_geometry_objects(geometries: list) -> list:
|
|
|
308
308
|
return result
|
|
309
309
|
|
|
310
310
|
|
|
311
|
-
def
|
|
312
|
-
|
|
313
|
-
|
|
311
|
+
def cmd_geometry_list(fmt: str, file_id: str, layer_id: str | None,
|
|
312
|
+
entity_types: str | None = None, features: str | None = None,
|
|
313
|
+
with_geometry: bool = False) -> None:
|
|
314
314
|
"""获取图层中的几何体对象(需 Dimine 运行)。
|
|
315
315
|
|
|
316
316
|
默认只返回几何体的基础信息(id、file、layer、feature、type、color、properties)。
|
|
@@ -366,7 +366,7 @@ def cmd_geometry_get(fmt: str, file_id: str, layer_id: str | None,
|
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
try:
|
|
369
|
-
result =
|
|
369
|
+
result = run_cli_async(_run())
|
|
370
370
|
output_result(result, fmt)
|
|
371
371
|
except Exception as e:
|
|
372
372
|
handle_sdk_error(e, fmt, "获取几何体失败")
|
|
@@ -423,7 +423,7 @@ def cmd_geometry_create(fmt: str, geometries: str | None = None,
|
|
|
423
423
|
}
|
|
424
424
|
|
|
425
425
|
try:
|
|
426
|
-
result =
|
|
426
|
+
result = run_cli_async(_run())
|
|
427
427
|
output_result(result, fmt)
|
|
428
428
|
except Exception as e:
|
|
429
429
|
handle_sdk_error(e, fmt, "创建几何体失败")
|
|
@@ -483,7 +483,7 @@ def cmd_geometry_update(fmt: str, geometries: str | None = None,
|
|
|
483
483
|
}
|
|
484
484
|
|
|
485
485
|
try:
|
|
486
|
-
result =
|
|
486
|
+
result = run_cli_async(_run())
|
|
487
487
|
output_result(result, fmt)
|
|
488
488
|
except Exception as e:
|
|
489
489
|
handle_sdk_error(e, fmt, "更新几何体失败")
|
|
@@ -557,7 +557,7 @@ def cmd_geometry_upsert(fmt: str, geometries: str | None = None,
|
|
|
557
557
|
}
|
|
558
558
|
|
|
559
559
|
try:
|
|
560
|
-
result =
|
|
560
|
+
result = run_cli_async(_run())
|
|
561
561
|
output_result(result, fmt)
|
|
562
562
|
except Exception as e:
|
|
563
563
|
handle_sdk_error(e, fmt, "处理几何体失败")
|
|
@@ -610,7 +610,7 @@ def register_commands(subparsers, format_parent) -> None:
|
|
|
610
610
|
|
|
611
611
|
|
|
612
612
|
COMMAND_HANDLERS = {
|
|
613
|
-
"geometry-list": lambda args:
|
|
613
|
+
"geometry-list": lambda args: cmd_geometry_list(args.format, args.file_id, args.layer_id,
|
|
614
614
|
args.entity_types, args.features,
|
|
615
615
|
args.with_geometry),
|
|
616
616
|
"geometry-create": lambda args: cmd_geometry_create(args.format, args.geometries,
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import asyncio
|
|
6
5
|
from typing import TYPE_CHECKING
|
|
7
6
|
|
|
8
7
|
if TYPE_CHECKING:
|
|
@@ -13,6 +12,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
13
12
|
get_client,
|
|
14
13
|
output_error,
|
|
15
14
|
output_result,
|
|
15
|
+
run_cli_async,
|
|
16
16
|
require_local_sdk,
|
|
17
17
|
require_remote_sdk,
|
|
18
18
|
safe_float,
|
|
@@ -170,7 +170,7 @@ def cmd_tunnel(fmt: str, section_type: int, width: float, height: float | None,
|
|
|
170
170
|
if err:
|
|
171
171
|
output_error(f"未提供 --center-lines,需要从 Dimine 获取选中线段: {err}", fmt)
|
|
172
172
|
try:
|
|
173
|
-
lines =
|
|
173
|
+
lines = run_cli_async(_fetch_center_lines_from_selection())
|
|
174
174
|
except Exception as e:
|
|
175
175
|
output_error(f"获取选中线段失败: {e}", fmt)
|
|
176
176
|
else:
|
|
@@ -228,7 +228,7 @@ def cmd_tunnel(fmt: str, section_type: int, width: float, height: float | None,
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
if import_to_dimine:
|
|
231
|
-
import_result =
|
|
231
|
+
import_result = run_cli_async(_import_to_dimine(geometries))
|
|
232
232
|
result["imported"] = import_result
|
|
233
233
|
else:
|
|
234
234
|
# 仅在明确不导入时返回简化模型摘要,不含原始坐标
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""图层相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
get_client,
|
|
7
5
|
handle_sdk_error,
|
|
@@ -9,6 +7,7 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
9
7
|
output_result,
|
|
10
8
|
raise_or_exit,
|
|
11
9
|
require_remote_sdk,
|
|
10
|
+
run_cli_async,
|
|
12
11
|
)
|
|
13
12
|
|
|
14
13
|
|
|
@@ -31,7 +30,7 @@ def cmd_layers(fmt: str, file_id: str | None) -> None:
|
|
|
31
30
|
return {"file_id": file_id, "layer_count": len(result), "layers": result}
|
|
32
31
|
|
|
33
32
|
try:
|
|
34
|
-
result =
|
|
33
|
+
result = run_cli_async(_run())
|
|
35
34
|
output_result(result, fmt)
|
|
36
35
|
except SystemExit:
|
|
37
36
|
raise
|
|
@@ -63,7 +62,7 @@ def cmd_layer_create(fmt: str, name: str, file_id: str | None) -> None:
|
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
try:
|
|
66
|
-
result =
|
|
65
|
+
result = run_cli_async(_run())
|
|
67
66
|
output_result(result, fmt)
|
|
68
67
|
except SystemExit:
|
|
69
68
|
raise
|
|
@@ -99,7 +98,7 @@ def cmd_layer_visibility(fmt: str, file_id: str, layer_name: str, action: str) -
|
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
try:
|
|
102
|
-
result =
|
|
101
|
+
result = run_cli_async(_run())
|
|
103
102
|
output_result(result, fmt)
|
|
104
103
|
except Exception as e:
|
|
105
104
|
handle_sdk_error(e, fmt, "设置图层可见性失败")
|
|
@@ -128,7 +127,7 @@ def cmd_layer_delete(fmt: str, name: str, file_id: str | None) -> None:
|
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
try:
|
|
131
|
-
result =
|
|
130
|
+
result = run_cli_async(_run())
|
|
132
131
|
output_result(result, fmt)
|
|
133
132
|
except SystemExit:
|
|
134
133
|
raise
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""文字标注相关 CLI 命令。"""
|
|
2
2
|
|
|
3
|
-
import asyncio
|
|
4
|
-
|
|
5
3
|
from dimine_python_sdk.cli.utils._utils import (
|
|
6
4
|
get_client,
|
|
7
5
|
handle_sdk_error,
|
|
@@ -11,8 +9,8 @@ from dimine_python_sdk.cli.utils._utils import (
|
|
|
11
9
|
parse_json_arg,
|
|
12
10
|
parse_point3d,
|
|
13
11
|
require_remote_sdk,
|
|
12
|
+
run_cli_async,
|
|
14
13
|
)
|
|
15
|
-
from dimine_python_sdk.models.types import TextInfo
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
def cmd_text_add(
|
|
@@ -37,6 +35,8 @@ def cmd_text_add(
|
|
|
37
35
|
text_infos = []
|
|
38
36
|
if geometries is not None:
|
|
39
37
|
# 批量模式:从 JSON 解析完整的 TextInfo 列表
|
|
38
|
+
from dimine_python_sdk.models.types import TextInfo
|
|
39
|
+
|
|
40
40
|
items = parse_json_arg(geometries, "geometries")
|
|
41
41
|
if not isinstance(items, list):
|
|
42
42
|
raise ValueError("--geometries 必须是 JSON 数组格式")
|
|
@@ -76,7 +76,7 @@ def cmd_text_add(
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
try:
|
|
79
|
-
result =
|
|
79
|
+
result = run_cli_async(_run())
|
|
80
80
|
output_result(result, fmt)
|
|
81
81
|
except Exception as e:
|
|
82
82
|
handle_sdk_error(e, fmt, "添加文字标注失败")
|
|
@@ -8,60 +8,77 @@
|
|
|
8
8
|
- JSON/文本输出格式化
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
|
-
import
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
12
13
|
import json
|
|
13
14
|
import os
|
|
14
15
|
import re
|
|
15
16
|
import sys
|
|
16
|
-
from typing import Any, Optional
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Optional
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from dimine_python_sdk.conn.conn_client import ConnClient
|
|
19
21
|
|
|
20
22
|
# ==============================================================================
|
|
21
23
|
# SDK 状态检查
|
|
22
24
|
# ==============================================================================
|
|
23
25
|
|
|
24
|
-
_SDK_AVAILABLE =
|
|
25
|
-
_local_sdk_available =
|
|
26
|
+
_SDK_AVAILABLE: bool | None = None # None = 尚未检查
|
|
27
|
+
_local_sdk_available: bool | None = None # None = 尚未检查
|
|
26
28
|
_import_error: str = ""
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
_SDK_AVAILABLE
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
29
|
+
_open_client: Any = None # 惰性缓存的 open_client 函数(私有,勿直接 import)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _check_remote_sdk() -> bool:
|
|
33
|
+
"""惰性检查远程 SDK 是否可用。首次调用时导入 conn 模块链并缓存结果。"""
|
|
34
|
+
global _SDK_AVAILABLE, _import_error, _open_client
|
|
35
|
+
if _SDK_AVAILABLE is not None:
|
|
36
|
+
return _SDK_AVAILABLE
|
|
37
|
+
try:
|
|
38
|
+
from dimine_python_sdk.conn.conn_client import open_client as _conn_open_client
|
|
39
|
+
_open_client = _conn_open_client
|
|
40
|
+
_SDK_AVAILABLE = True
|
|
41
|
+
except ImportError as e:
|
|
42
|
+
_import_error = str(e)
|
|
43
|
+
_SDK_AVAILABLE = False
|
|
44
|
+
except Exception as e:
|
|
45
|
+
_import_error = str(e)
|
|
46
|
+
_SDK_AVAILABLE = False
|
|
47
|
+
return _SDK_AVAILABLE
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _check_local_sdk() -> bool:
|
|
51
|
+
"""惰性检查本地 SDK 是否可用。首次调用时加载 DmPyBindInterface.pyd 并缓存结果。"""
|
|
52
|
+
global _local_sdk_available
|
|
53
|
+
if _local_sdk_available is not None:
|
|
54
|
+
return _local_sdk_available
|
|
55
|
+
try:
|
|
56
|
+
from dimine_python_sdk.lib.native._core import Dm # noqa: F401
|
|
57
|
+
_local_sdk_available = True
|
|
58
|
+
except (ImportError, Exception):
|
|
59
|
+
_local_sdk_available = False
|
|
60
|
+
return _local_sdk_available
|
|
45
61
|
|
|
46
62
|
|
|
47
63
|
def is_remote_sdk_available() -> bool:
|
|
48
64
|
"""返回远程 SDK 是否可用。"""
|
|
49
|
-
return
|
|
65
|
+
return _check_remote_sdk()
|
|
50
66
|
|
|
51
67
|
|
|
52
68
|
def is_local_sdk_available() -> bool:
|
|
53
69
|
"""返回本地 SDK 是否可用。"""
|
|
54
|
-
return
|
|
70
|
+
return _check_local_sdk()
|
|
55
71
|
|
|
56
72
|
|
|
57
73
|
def get_import_error() -> str:
|
|
58
74
|
"""返回 SDK 导入错误信息。"""
|
|
75
|
+
_check_remote_sdk() # 确保已检查
|
|
59
76
|
return _import_error
|
|
60
77
|
|
|
61
78
|
|
|
62
79
|
def require_remote_sdk() -> str:
|
|
63
80
|
"""返回远程 SDK 不可用时的错误消息(空字符串表示可用)。"""
|
|
64
|
-
if
|
|
81
|
+
if _check_remote_sdk():
|
|
65
82
|
return ""
|
|
66
83
|
return (
|
|
67
84
|
f"Dimine Python SDK 不可用: {_import_error}\n\n"
|
|
@@ -75,7 +92,7 @@ def require_remote_sdk() -> str:
|
|
|
75
92
|
|
|
76
93
|
def require_local_sdk() -> str:
|
|
77
94
|
"""返回本地 SDK 不可用时的错误消息(空字符串表示可用)。"""
|
|
78
|
-
if
|
|
95
|
+
if _check_local_sdk():
|
|
79
96
|
return ""
|
|
80
97
|
return (
|
|
81
98
|
f"Dimine Python SDK 本地模块不可用。\n"
|
|
@@ -94,12 +111,37 @@ _client_instance: Optional[Any] = None
|
|
|
94
111
|
_client_loop_id: Optional[int] = None
|
|
95
112
|
|
|
96
113
|
|
|
114
|
+
async def _run_with_cleanup(coro):
|
|
115
|
+
"""包装协程,确保在事件循环关闭前清理 WebSocket 客户端。
|
|
116
|
+
|
|
117
|
+
防止 Windows ProactorEventLoop 关闭时因未清理的 IOCP 句柄导致
|
|
118
|
+
进程退出码变为 0xC0000409 (-1073740791)。
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
return await coro
|
|
122
|
+
finally:
|
|
123
|
+
await close_client()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def run_cli_async(coro):
|
|
127
|
+
"""CLI 命令统一入口:运行异步操作并确保客户端清理。
|
|
128
|
+
|
|
129
|
+
替代裸 asyncio.run(),在所有 CLI 命令的 _run() 之后调用。
|
|
130
|
+
用法:result = run_cli_async(_run())
|
|
131
|
+
"""
|
|
132
|
+
import asyncio
|
|
133
|
+
|
|
134
|
+
return asyncio.run(_run_with_cleanup(coro))
|
|
135
|
+
|
|
136
|
+
|
|
97
137
|
async def get_client()->ConnClient:
|
|
98
138
|
"""获取或创建共享的 WebSocket 客户端,避免每次操作都建立新连接。
|
|
99
139
|
|
|
100
140
|
如果检测到事件循环已变更(如 asyncio.run() 多次调用场景),
|
|
101
141
|
自动丢弃旧连接并重新建立,避免跨事件循环使用 WebSocket 导致的 RuntimeError。
|
|
102
142
|
"""
|
|
143
|
+
import asyncio
|
|
144
|
+
|
|
103
145
|
global _client_ctx, _client_instance, _client_loop_id
|
|
104
146
|
|
|
105
147
|
# 检测事件循环是否变更(asyncio.run() 每次创建新的事件循环)
|
|
@@ -115,7 +157,7 @@ async def get_client()->ConnClient:
|
|
|
115
157
|
_client_loop_id = None
|
|
116
158
|
|
|
117
159
|
if _client_instance is None:
|
|
118
|
-
_client_ctx =
|
|
160
|
+
_client_ctx = _open_client()
|
|
119
161
|
_client_instance = await _client_ctx.__aenter__()
|
|
120
162
|
_client_loop_id = id(asyncio.get_running_loop())
|
|
121
163
|
return _client_instance
|
|
@@ -19,6 +19,7 @@ class BaseConnClient:
|
|
|
19
19
|
model_config = {"arbitrary_types_allowed": True}
|
|
20
20
|
|
|
21
21
|
url: Optional[str] = "ws://127.0.0.1:8000"
|
|
22
|
+
max_size: Optional[int] = None
|
|
22
23
|
is_connected: Optional[bool] = False
|
|
23
24
|
pending_requests: Dict[str, Tuple[asyncio.Future, Callable]] = field(default_factory=dict)
|
|
24
25
|
event_handlers: Dict[str, List[Callable]] = field(default_factory=dict)
|
|
@@ -106,7 +107,10 @@ class BaseConnClient:
|
|
|
106
107
|
|
|
107
108
|
async def connect(self)-> bool:
|
|
108
109
|
try:
|
|
109
|
-
|
|
110
|
+
connect_kwargs: Dict[str, Any] = {"close_timeout": 1}
|
|
111
|
+
if self.max_size is not None:
|
|
112
|
+
connect_kwargs["max_size"] = self.max_size
|
|
113
|
+
self.websocket = await websockets.connect(self.url, **connect_kwargs)
|
|
110
114
|
self.is_connected = True
|
|
111
115
|
logger.info("success connected to %s", self.url)
|
|
112
116
|
if self.receive_task is None or self.receive_task.done():
|