dsh-engineering-ui 0.0.0-stage → 1.5.1
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.
- package/LICENSE +21 -0
- package/README.md +772 -3
- package/engineering/plugins/dsh-engineering-ui/cordis.patch.yml +5 -0
- package/engineering/plugins/dsh-engineering-ui/icon.svg +14 -0
- package/engineering/plugins/dsh-engineering-ui/lib/client.js +8257 -0
- package/engineering/plugins/dsh-engineering-ui/lib/defense-sign.js +1552 -0
- package/engineering/plugins/dsh-engineering-ui/lib/index.js +5634 -0
- package/engineering/plugins/dsh-engineering-ui/lib/job-wait-guard.js +236 -0
- package/engineering/plugins/dsh-engineering-ui/lib/mem-guard.js +331 -0
- package/engineering/plugins/dsh-engineering-ui/lib/webots.js +114 -0
- package/engineering/plugins/dsh-engineering-ui/locale/en.json +6 -0
- package/engineering/plugins/dsh-engineering-ui/locale/zh.json +6 -0
- package/engineering/plugins/dsh-engineering-ui/preset-engineering.patch.yml +601 -0
- package/engineering/skills/assembly-orchestration/SKILL.md +651 -0
- package/engineering/skills/cad-workflow/SKILL.md +132 -0
- package/engineering/skills/mode-selection/SKILL.md +660 -0
- package/engineering/skills/physics-in-loop/SKILL.md +238 -0
- package/engineering/skills/physics-in-loop/references/feadocs.md +142 -0
- package/engineering/skills/solidworks-bridge/SKILL.md +445 -0
- package/engineering/skills/sw-design/SKILL.md +112 -0
- package/engineering/skills/sw-design/references/autocad-workflow.md +81 -0
- package/engineering/skills/sw-design/references/gbt-drafting.md +71 -0
- package/engineering/skills/sw-to-cad/SKILL.md +121 -0
- package/engineering/skills/webots-simulation/SKILL.md +58 -0
- package/engineering/tools/LICENSE.Auto-SolidWorks +21 -0
- package/engineering/tools/_plugin_paths.py +93 -0
- package/engineering/tools/_root.py +330 -0
- package/engineering/tools/_store.py +446 -0
- package/engineering/tools/ac_bridge.py +459 -0
- package/engineering/tools/ac_rules.json +21 -0
- package/engineering/tools/ac_validate.py +699 -0
- package/engineering/tools/ask_user.py +887 -0
- package/engineering/tools/build_arm1.py +46 -0
- package/engineering/tools/build_assembly.py +94 -0
- package/engineering/tools/build_base.py +57 -0
- package/engineering/tools/build_housing.py +70 -0
- package/engineering/tools/build_structural.py +43 -0
- package/engineering/tools/build_transmission.py +69 -0
- package/engineering/tools/cadx.lsp +128 -0
- package/engineering/tools/choice_contract.py +61 -0
- package/engineering/tools/conn_state.py +1671 -0
- package/engineering/tools/defense_gate.py +3374 -0
- package/engineering/tools/drawing_input.py +314 -0
- package/engineering/tools/env_check.py +467 -0
- package/engineering/tools/examples/build_axis.py +142 -0
- package/engineering/tools/examples/build_cup.py +56 -0
- package/engineering/tools/examples/build_plate.py +58 -0
- package/engineering/tools/examples/build_sphere.py +30 -0
- package/engineering/tools/examples/build_transition_duct.py +381 -0
- package/engineering/tools/gate_integrity.py +401 -0
- package/engineering/tools/mode2_flow.py +3589 -0
- package/engineering/tools/mode_checker.py +77 -0
- package/engineering/tools/mode_gate.py +8013 -0
- package/engineering/tools/part_registry.py +4266 -0
- package/engineering/tools/physics/__init__.py +50 -0
- package/engineering/tools/physics/_compat.py +12 -0
- package/engineering/tools/physics/design_state.py +142 -0
- package/engineering/tools/physics/domain_validator.py +751 -0
- package/engineering/tools/physics/fatigue.py +489 -0
- package/engineering/tools/physics/fea_solver.py +1163 -0
- package/engineering/tools/physics/feapy_solver.py +314 -0
- package/engineering/tools/physics/gb_materials.json +1228 -0
- package/engineering/tools/physics/geometry_gate.py +337 -0
- package/engineering/tools/physics/load_case.py +951 -0
- package/engineering/tools/physics/material_db.py +874 -0
- package/engineering/tools/physics/mesh_adapter.py +321 -0
- package/engineering/tools/physics/refine_rules.py +272 -0
- package/engineering/tools/physics/rules/gb_base_rules.json +377 -0
- package/engineering/tools/physics/rules/gb_drafting_rules.json +57 -0
- package/engineering/tools/physics/rules/gb_electrical_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_fasteners_rules.json +73 -0
- package/engineering/tools/physics/rules/gb_housing_rules.json +41 -0
- package/engineering/tools/physics/rules/gb_manufacturing_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_material_verify_rules.json +72 -0
- package/engineering/tools/physics/rules/gb_materials_rules.json +377 -0
- package/engineering/tools/physics/rules/gb_safety_rules.json +89 -0
- package/engineering/tools/physics/rules/gb_springs_rules.json +41 -0
- package/engineering/tools/physics/rules/gb_standards_registry.json +1646 -0
- package/engineering/tools/physics/rules/gb_structural_rules.json +201 -0
- package/engineering/tools/physics/rules/gb_surface_rules.json +25 -0
- package/engineering/tools/physics/rules/gb_transmission_rules.json +233 -0
- package/engineering/tools/physics/rules/housing_rules.json +48 -0
- package/engineering/tools/physics/rules/manifest.json +54 -0
- package/engineering/tools/physics/rules/mold_rules.json +48 -0
- package/engineering/tools/physics/rules/structural_rules.json +56 -0
- package/engineering/tools/physics/rules/transmission_rules.json +48 -0
- package/engineering/tools/physics/simulation_report.py +829 -0
- package/engineering/tools/physics_bridge.py +2685 -0
- package/engineering/tools/profile-manifest.ps1 +72 -0
- package/engineering/tools/run_regression.py +109 -0
- package/engineering/tools/settings.py +419 -0
- package/engineering/tools/solidworks-modeling.md +519 -0
- package/engineering/tools/sw_bridge.py +8215 -0
- package/engineering/tools/sw_guard.py +1066 -0
- package/engineering/tools/sw_surface.py +87 -0
- package/engineering/tools/swapi.py +14766 -0
- package/engineering/tools/verify_sync.py +218 -0
- package/engineering/tools/visual_check.py +2589 -0
- package/engineering/tools/webots_env.py +121 -0
- package/engineering/tools/webots_mcp/__init__.py +3 -0
- package/engineering/tools/webots_mcp/__main__.py +3 -0
- package/engineering/tools/webots_mcp/assembly.py +391 -0
- package/engineering/tools/webots_mcp/bootstrap.py +55 -0
- package/engineering/tools/webots_mcp/controller_worker.py +124 -0
- package/engineering/tools/webots_mcp/evidence.py +288 -0
- package/engineering/tools/webots_mcp/fake_worker.py +69 -0
- package/engineering/tools/webots_mcp/fault_resources.py +121 -0
- package/engineering/tools/webots_mcp/host.py +45 -0
- package/engineering/tools/webots_mcp/ledger.py +73 -0
- package/engineering/tools/webots_mcp/owned_job.py +70 -0
- package/engineering/tools/webots_mcp/physical_gate.py +187 -0
- package/engineering/tools/webots_mcp/physics.py +152 -0
- package/engineering/tools/webots_mcp/pipeline.py +108 -0
- package/engineering/tools/webots_mcp/recipes.py +129 -0
- package/engineering/tools/webots_mcp/repair.py +132 -0
- package/engineering/tools/webots_mcp/repair_trial.py +375 -0
- package/engineering/tools/webots_mcp/runtime.py +311 -0
- package/engineering/tools/webots_mcp/server.py +217 -0
- package/engineering/tools/webots_mcp/sw_export.py +509 -0
- package/engineering/tools/webots_mcp/transport.py +156 -0
- package/engineering/tools/workflow_gate.py +6134 -0
- package/package.json +66 -6
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Private, bounded, generation-scoped Webots external-controller transport."""
|
|
2
|
+
import asyncio
|
|
3
|
+
import hashlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import re
|
|
8
|
+
import secrets
|
|
9
|
+
import socket
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from .fault_resources import budget_check, sample_job
|
|
13
|
+
from .owned_job import OwnedJob
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Transport:
|
|
17
|
+
provenance = "real-webots"
|
|
18
|
+
|
|
19
|
+
def __init__(self, state, root, config, run,generation):
|
|
20
|
+
self.root = root
|
|
21
|
+
self.home = Path(config["webots_home"]).resolve(strict=True)
|
|
22
|
+
self.config = config
|
|
23
|
+
self.run = run
|
|
24
|
+
self.generation = generation
|
|
25
|
+
from .evidence import _no_reparse
|
|
26
|
+
self.dir = state / "webots_runs" / run / "worker" / str(generation)
|
|
27
|
+
for path in (state/"webots_runs",state/"webots_runs"/run,self.dir.parent,self.dir):
|
|
28
|
+
_no_reparse(path)
|
|
29
|
+
self.dir.mkdir(parents=True, exist_ok=False)
|
|
30
|
+
self.job = OwnedJob(config["limits"]["process_count"])
|
|
31
|
+
self.writer = self.listener = self.previous = None
|
|
32
|
+
self.identity = {}
|
|
33
|
+
self.connected = False
|
|
34
|
+
self.quitting = False
|
|
35
|
+
|
|
36
|
+
async def launch(self, args):
|
|
37
|
+
world = (self.root / args["world_path"]).resolve(strict=True)
|
|
38
|
+
if not world.is_relative_to(self.root) or world.stat().st_size > 16 * 1024**2:
|
|
39
|
+
raise ValueError("World outside supported input scope")
|
|
40
|
+
binding = self.config["worlds"][args["world_path"]]
|
|
41
|
+
digest = hashlib.sha256(world.read_bytes()).hexdigest()
|
|
42
|
+
if not re.fullmatch(r"[0-9a-f]{64}",binding["model_sha256"]) or binding["sha256"] != digest or not re.fullmatch(r"[A-Za-z0-9_-]{1,95}",binding["robot"]):
|
|
43
|
+
raise ValueError("Pinned world/robot mismatch")
|
|
44
|
+
def file_digest(path):
|
|
45
|
+
with path.open("rb") as source:
|
|
46
|
+
return hashlib.file_digest(source,"sha256").hexdigest()
|
|
47
|
+
for exe in ("webots.exe","webots-controller.exe"):
|
|
48
|
+
path = self.home / "msys64/mingw64/bin" / exe
|
|
49
|
+
if await asyncio.to_thread(file_digest,path) != self.config["binary_sha256"][exe]:
|
|
50
|
+
raise ValueError("Runtime binary hash differs from trusted configuration")
|
|
51
|
+
self.kernel = (self.home/self.config["kernel_relative_path"]).resolve(strict=True)
|
|
52
|
+
if (not self.kernel.is_relative_to(self.home)
|
|
53
|
+
or await asyncio.to_thread(file_digest,self.kernel) != self.config["kernel_sha256"]):
|
|
54
|
+
raise ValueError("Unpinned native kernel executable")
|
|
55
|
+
robot = binding["robot"]
|
|
56
|
+
with socket.socket() as reserve:
|
|
57
|
+
reserve.bind(("127.0.0.1",0))
|
|
58
|
+
port = reserve.getsockname()[1]
|
|
59
|
+
token = secrets.token_hex(32)
|
|
60
|
+
ready = asyncio.get_running_loop().create_future()
|
|
61
|
+
|
|
62
|
+
async def peer(reader, writer):
|
|
63
|
+
try:
|
|
64
|
+
msg = json.loads(await asyncio.wait_for(reader.readline(),2))
|
|
65
|
+
if (msg["token"] != token or msg["run_id"] != self.run or msg["port"] != port
|
|
66
|
+
or msg["generation"] != self.generation or msg["robot"] != robot or msg["world_sha256"] != digest
|
|
67
|
+
or not Path(msg["world"]).samefile(world) or ready.done()):
|
|
68
|
+
raise ValueError("Identity mismatch")
|
|
69
|
+
identity = self.job.identity(msg["pid"])
|
|
70
|
+
if not any(Path(identity["exe"]).samefile(path) for path in (sys.executable,sys._base_executable)):
|
|
71
|
+
raise PermissionError("Controller interpreter mismatch")
|
|
72
|
+
self.identity.update(controller=identity,node_id=msg["node_id"],basic_time_step_ms=msg["basic_time_step_ms"])
|
|
73
|
+
self.reader, self.writer = reader, writer
|
|
74
|
+
ready.set_result(msg["time"])
|
|
75
|
+
except Exception:
|
|
76
|
+
writer.close()
|
|
77
|
+
|
|
78
|
+
self.listener = await asyncio.start_server(peer,"127.0.0.1",0,limit=65536,backlog=2)
|
|
79
|
+
rpc = self.listener.sockets[0].getsockname()[1]
|
|
80
|
+
env = dict(os.environ, WEBOTS_HOME=str(self.home),A04_RUN=self.run,A04_OUTPUT=str(self.dir),A04_WORLD=str(world),
|
|
81
|
+
A04_GENERATION=str(self.generation),A04_HASH=digest,A04_ROBOT=robot,A04_PORT=str(port),A04_RPC_PORT=str(rpc),A04_TOKEN=token)
|
|
82
|
+
self.identity.update(world=str(world),world_sha256=digest,model_sha256=binding["model_sha256"],
|
|
83
|
+
run_id=self.run,generation=self.generation,port=port,robot=robot,
|
|
84
|
+
bootstrap=self.job.spawn([sys.executable,"-B",str(Path(__file__).with_name("bootstrap.py"))],env,self.dir))
|
|
85
|
+
def save_identity():
|
|
86
|
+
tmp = self.dir/"identity.tmp"
|
|
87
|
+
with tmp.open("w",encoding="utf-8") as f:
|
|
88
|
+
json.dump(self.identity,f,allow_nan=False)
|
|
89
|
+
f.flush()
|
|
90
|
+
os.fsync(f.fileno())
|
|
91
|
+
tmp.replace(self.dir/"identity.json")
|
|
92
|
+
save_identity()
|
|
93
|
+
sim_time = await ready
|
|
94
|
+
membership = self.sample()
|
|
95
|
+
kernels = [p for p in membership["processes"] if Path(p["exe"]).samefile(self.kernel)]
|
|
96
|
+
if not membership["complete"] or len(kernels)!=1:
|
|
97
|
+
raise PermissionError("Unique native kernel identity not verified in private Job")
|
|
98
|
+
self.identity.update(kernel=kernels[0],owned_processes=membership["processes"])
|
|
99
|
+
save_identity()
|
|
100
|
+
self.listener.close()
|
|
101
|
+
self.connected = True
|
|
102
|
+
return dict(data=dict(self.identity),completed_steps=0,sim_time=sim_time)
|
|
103
|
+
|
|
104
|
+
def sample(self):
|
|
105
|
+
if "bootstrap" not in self.identity:
|
|
106
|
+
return None
|
|
107
|
+
sample = sample_job(self.job.handle,previous=self.previous)
|
|
108
|
+
self.previous = sample
|
|
109
|
+
check = budget_check(sample,self.config["limits"])
|
|
110
|
+
if check["within_budget"] is not True:
|
|
111
|
+
raise RuntimeError("Resource budget " + str(check))
|
|
112
|
+
if "kernel" in self.identity and not self.quitting:
|
|
113
|
+
known=self.identity["kernel"]
|
|
114
|
+
if not any((p["pid"],p["creation_time"],p["exe"])==(known["pid"],known["creation_time"],known["exe"])
|
|
115
|
+
for p in sample["processes"]):
|
|
116
|
+
raise ConnectionError("Native kernel identity lost")
|
|
117
|
+
if not self.quitting and (not sample["process_count"] or (self.connected and self.reader.at_eof())):
|
|
118
|
+
raise ConnectionError("Owned runtime disconnected")
|
|
119
|
+
return sample
|
|
120
|
+
|
|
121
|
+
async def command(self,name,args,progress):
|
|
122
|
+
if not self.connected:
|
|
123
|
+
raise ConnectionError("No verified controller")
|
|
124
|
+
self.quitting = name == "sim_shutdown"
|
|
125
|
+
self.writer.write(json.dumps(dict(name=name,args=args),allow_nan=False).encode()+b"\n")
|
|
126
|
+
await self.writer.drain()
|
|
127
|
+
while True:
|
|
128
|
+
raw = await self.reader.readline()
|
|
129
|
+
if not raw:
|
|
130
|
+
raise ConnectionError("Controller disconnected")
|
|
131
|
+
msg = json.loads(raw)
|
|
132
|
+
if msg.get("progress") is True:
|
|
133
|
+
progress(msg["completed_steps"],msg["sim_time"])
|
|
134
|
+
elif msg.get("done") is True:
|
|
135
|
+
if name == "sim_shutdown":
|
|
136
|
+
while self.job.active():
|
|
137
|
+
await asyncio.sleep(.05)
|
|
138
|
+
return {key:msg[key] for key in ("completed_steps","sim_time","data")}
|
|
139
|
+
else:
|
|
140
|
+
raise ValueError("Invalid controller response")
|
|
141
|
+
|
|
142
|
+
async def close(self):
|
|
143
|
+
self.connected = False
|
|
144
|
+
if self.listener:
|
|
145
|
+
self.listener.close()
|
|
146
|
+
if self.writer:
|
|
147
|
+
self.writer.close()
|
|
148
|
+
# 【Windows 句柄释放】只 close() 不 wait_closed() 时,管道句柄要等
|
|
149
|
+
# 事件循环下一轮才真正关掉;此时上层(测试的 TemporaryDirectory
|
|
150
|
+
# 清理 / 换机后的目录删除)会拿到 WinError 32(文件被占用)。
|
|
151
|
+
# 这里显式等传输层关闭,保证 close() 返回后句柄已释放。
|
|
152
|
+
try:
|
|
153
|
+
await self.writer.wait_closed()
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
await asyncio.to_thread(self.job.stop)
|