dsh-engineering-ui 0.0.0-stage → 1.5.0

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 (122) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +772 -3
  3. package/engineering/plugins/dsh-engineering-ui/cordis.patch.yml +5 -0
  4. package/engineering/plugins/dsh-engineering-ui/icon.svg +14 -0
  5. package/engineering/plugins/dsh-engineering-ui/lib/client.js +8259 -0
  6. package/engineering/plugins/dsh-engineering-ui/lib/defense-sign.js +1552 -0
  7. package/engineering/plugins/dsh-engineering-ui/lib/index.js +5633 -0
  8. package/engineering/plugins/dsh-engineering-ui/lib/job-wait-guard.js +236 -0
  9. package/engineering/plugins/dsh-engineering-ui/lib/mem-guard.js +331 -0
  10. package/engineering/plugins/dsh-engineering-ui/lib/webots.js +114 -0
  11. package/engineering/plugins/dsh-engineering-ui/locale/en.json +6 -0
  12. package/engineering/plugins/dsh-engineering-ui/locale/zh.json +6 -0
  13. package/engineering/plugins/dsh-engineering-ui/preset-engineering.patch.yml +601 -0
  14. package/engineering/skills/assembly-orchestration/SKILL.md +651 -0
  15. package/engineering/skills/cad-workflow/SKILL.md +132 -0
  16. package/engineering/skills/mode-selection/SKILL.md +660 -0
  17. package/engineering/skills/physics-in-loop/SKILL.md +238 -0
  18. package/engineering/skills/physics-in-loop/references/feadocs.md +142 -0
  19. package/engineering/skills/solidworks-bridge/SKILL.md +445 -0
  20. package/engineering/skills/sw-design/SKILL.md +112 -0
  21. package/engineering/skills/sw-design/references/autocad-workflow.md +81 -0
  22. package/engineering/skills/sw-design/references/gbt-drafting.md +71 -0
  23. package/engineering/skills/sw-to-cad/SKILL.md +121 -0
  24. package/engineering/skills/webots-simulation/SKILL.md +58 -0
  25. package/engineering/tools/LICENSE.Auto-SolidWorks +21 -0
  26. package/engineering/tools/_plugin_paths.py +93 -0
  27. package/engineering/tools/_root.py +330 -0
  28. package/engineering/tools/_store.py +446 -0
  29. package/engineering/tools/ac_bridge.py +459 -0
  30. package/engineering/tools/ac_rules.json +21 -0
  31. package/engineering/tools/ac_validate.py +699 -0
  32. package/engineering/tools/ask_user.py +887 -0
  33. package/engineering/tools/build_arm1.py +46 -0
  34. package/engineering/tools/build_assembly.py +94 -0
  35. package/engineering/tools/build_base.py +57 -0
  36. package/engineering/tools/build_housing.py +70 -0
  37. package/engineering/tools/build_structural.py +43 -0
  38. package/engineering/tools/build_transmission.py +69 -0
  39. package/engineering/tools/cadx.lsp +128 -0
  40. package/engineering/tools/choice_contract.py +61 -0
  41. package/engineering/tools/conn_state.py +1671 -0
  42. package/engineering/tools/defense_gate.py +3374 -0
  43. package/engineering/tools/drawing_input.py +314 -0
  44. package/engineering/tools/env_check.py +467 -0
  45. package/engineering/tools/examples/build_axis.py +142 -0
  46. package/engineering/tools/examples/build_cup.py +56 -0
  47. package/engineering/tools/examples/build_plate.py +58 -0
  48. package/engineering/tools/examples/build_sphere.py +30 -0
  49. package/engineering/tools/examples/build_transition_duct.py +381 -0
  50. package/engineering/tools/gate_integrity.py +401 -0
  51. package/engineering/tools/mode2_flow.py +3589 -0
  52. package/engineering/tools/mode_checker.py +77 -0
  53. package/engineering/tools/mode_gate.py +8013 -0
  54. package/engineering/tools/part_registry.py +4266 -0
  55. package/engineering/tools/physics/__init__.py +50 -0
  56. package/engineering/tools/physics/_compat.py +12 -0
  57. package/engineering/tools/physics/design_state.py +142 -0
  58. package/engineering/tools/physics/domain_validator.py +751 -0
  59. package/engineering/tools/physics/fatigue.py +489 -0
  60. package/engineering/tools/physics/fea_solver.py +1163 -0
  61. package/engineering/tools/physics/feapy_solver.py +314 -0
  62. package/engineering/tools/physics/gb_materials.json +1228 -0
  63. package/engineering/tools/physics/geometry_gate.py +337 -0
  64. package/engineering/tools/physics/load_case.py +951 -0
  65. package/engineering/tools/physics/material_db.py +874 -0
  66. package/engineering/tools/physics/mesh_adapter.py +321 -0
  67. package/engineering/tools/physics/refine_rules.py +272 -0
  68. package/engineering/tools/physics/rules/gb_base_rules.json +377 -0
  69. package/engineering/tools/physics/rules/gb_drafting_rules.json +57 -0
  70. package/engineering/tools/physics/rules/gb_electrical_rules.json +25 -0
  71. package/engineering/tools/physics/rules/gb_fasteners_rules.json +73 -0
  72. package/engineering/tools/physics/rules/gb_housing_rules.json +41 -0
  73. package/engineering/tools/physics/rules/gb_manufacturing_rules.json +25 -0
  74. package/engineering/tools/physics/rules/gb_material_verify_rules.json +72 -0
  75. package/engineering/tools/physics/rules/gb_materials_rules.json +377 -0
  76. package/engineering/tools/physics/rules/gb_safety_rules.json +89 -0
  77. package/engineering/tools/physics/rules/gb_springs_rules.json +41 -0
  78. package/engineering/tools/physics/rules/gb_standards_registry.json +1646 -0
  79. package/engineering/tools/physics/rules/gb_structural_rules.json +201 -0
  80. package/engineering/tools/physics/rules/gb_surface_rules.json +25 -0
  81. package/engineering/tools/physics/rules/gb_transmission_rules.json +233 -0
  82. package/engineering/tools/physics/rules/housing_rules.json +48 -0
  83. package/engineering/tools/physics/rules/manifest.json +54 -0
  84. package/engineering/tools/physics/rules/mold_rules.json +48 -0
  85. package/engineering/tools/physics/rules/structural_rules.json +56 -0
  86. package/engineering/tools/physics/rules/transmission_rules.json +48 -0
  87. package/engineering/tools/physics/simulation_report.py +829 -0
  88. package/engineering/tools/physics_bridge.py +2685 -0
  89. package/engineering/tools/profile-manifest.ps1 +72 -0
  90. package/engineering/tools/run_regression.py +109 -0
  91. package/engineering/tools/settings.py +419 -0
  92. package/engineering/tools/solidworks-modeling.md +519 -0
  93. package/engineering/tools/sw_bridge.py +8215 -0
  94. package/engineering/tools/sw_guard.py +1066 -0
  95. package/engineering/tools/sw_surface.py +87 -0
  96. package/engineering/tools/swapi.py +14766 -0
  97. package/engineering/tools/verify_sync.py +218 -0
  98. package/engineering/tools/visual_check.py +2589 -0
  99. package/engineering/tools/webots_env.py +121 -0
  100. package/engineering/tools/webots_mcp/__init__.py +3 -0
  101. package/engineering/tools/webots_mcp/__main__.py +3 -0
  102. package/engineering/tools/webots_mcp/assembly.py +391 -0
  103. package/engineering/tools/webots_mcp/bootstrap.py +55 -0
  104. package/engineering/tools/webots_mcp/controller_worker.py +124 -0
  105. package/engineering/tools/webots_mcp/evidence.py +288 -0
  106. package/engineering/tools/webots_mcp/fake_worker.py +69 -0
  107. package/engineering/tools/webots_mcp/fault_resources.py +121 -0
  108. package/engineering/tools/webots_mcp/host.py +45 -0
  109. package/engineering/tools/webots_mcp/ledger.py +73 -0
  110. package/engineering/tools/webots_mcp/owned_job.py +70 -0
  111. package/engineering/tools/webots_mcp/physical_gate.py +187 -0
  112. package/engineering/tools/webots_mcp/physics.py +152 -0
  113. package/engineering/tools/webots_mcp/pipeline.py +108 -0
  114. package/engineering/tools/webots_mcp/recipes.py +129 -0
  115. package/engineering/tools/webots_mcp/repair.py +132 -0
  116. package/engineering/tools/webots_mcp/repair_trial.py +375 -0
  117. package/engineering/tools/webots_mcp/runtime.py +311 -0
  118. package/engineering/tools/webots_mcp/server.py +217 -0
  119. package/engineering/tools/webots_mcp/sw_export.py +509 -0
  120. package/engineering/tools/webots_mcp/transport.py +156 -0
  121. package/engineering/tools/workflow_gate.py +6134 -0
  122. package/package.json +64 -4
@@ -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)