@aryacode/dsh-engineering 0.0.0-stage → 0.3.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 (281) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -2
  4. package/cordis.patch.yml +122 -0
  5. package/locale/en.json +6 -0
  6. package/locale/zh.json +6 -0
  7. package/package.json +77 -3
  8. package/vendor/ftview/README.md +55 -0
  9. package/vendor/ftview/SKILL.md +74 -0
  10. package/vendor/ftview/lib/checks.d.ts +27 -0
  11. package/vendor/ftview/lib/checks.js +39 -0
  12. package/vendor/ftview/lib/core.d.ts +208 -0
  13. package/vendor/ftview/lib/core.js +160 -0
  14. package/vendor/ftview/lib/edit/drafts.d.ts +49 -0
  15. package/vendor/ftview/lib/edit/drafts.js +84 -0
  16. package/vendor/ftview/lib/edit/operations.d.ts +62 -0
  17. package/vendor/ftview/lib/edit/operations.js +126 -0
  18. package/vendor/ftview/lib/edit/prepare.d.ts +38 -0
  19. package/vendor/ftview/lib/edit/prepare.js +133 -0
  20. package/vendor/ftview/lib/edit/tools.d.ts +25 -0
  21. package/vendor/ftview/lib/edit/tools.js +198 -0
  22. package/vendor/ftview/lib/edit.d.ts +30 -0
  23. package/vendor/ftview/lib/edit.js +38 -0
  24. package/vendor/ftview/lib/index.d.ts +31 -0
  25. package/vendor/ftview/lib/index.js +36 -0
  26. package/vendor/ftview/lib/model.d.ts +80 -0
  27. package/vendor/ftview/lib/model.js +267 -0
  28. package/vendor/ftview/lib/session.d.ts +48 -0
  29. package/vendor/ftview/lib/session.js +98 -0
  30. package/vendor/ftview/lib/skill.d.ts +29 -0
  31. package/vendor/ftview/lib/skill.js +43 -0
  32. package/vendor/ftview/lib/studio/discover.d.ts +36 -0
  33. package/vendor/ftview/lib/studio/discover.js +87 -0
  34. package/vendor/ftview/lib/studio/preflight.d.ts +51 -0
  35. package/vendor/ftview/lib/studio/preflight.js +64 -0
  36. package/vendor/ftview/lib/studio/tools.d.ts +15 -0
  37. package/vendor/ftview/lib/studio/tools.js +83 -0
  38. package/vendor/ftview/lib/studio.d.ts +30 -0
  39. package/vendor/ftview/lib/studio.js +37 -0
  40. package/vendor/ftview/lib/tools.d.ts +14 -0
  41. package/vendor/ftview/lib/tools.js +202 -0
  42. package/vendor/l5x/README.md +62 -0
  43. package/vendor/l5x/SKILL.md +81 -0
  44. package/vendor/l5x/lib/approvals.d.ts +50 -0
  45. package/vendor/l5x/lib/approvals.js +93 -0
  46. package/vendor/l5x/lib/client.js +412 -0
  47. package/vendor/l5x/lib/client.js.map +1 -0
  48. package/vendor/l5x/lib/core/errors.d.ts +38 -0
  49. package/vendor/l5x/lib/core/errors.js +48 -0
  50. package/vendor/l5x/lib/core/params.d.ts +49 -0
  51. package/vendor/l5x/lib/core/params.js +39 -0
  52. package/vendor/l5x/lib/core/result.d.ts +46 -0
  53. package/vendor/l5x/lib/core/result.js +21 -0
  54. package/vendor/l5x/lib/core/tool.d.ts +102 -0
  55. package/vendor/l5x/lib/core/tool.js +19 -0
  56. package/vendor/l5x/lib/drafts.d.ts +66 -0
  57. package/vendor/l5x/lib/drafts.js +98 -0
  58. package/vendor/l5x/lib/editing.d.ts +30 -0
  59. package/vendor/l5x/lib/editing.js +42 -0
  60. package/vendor/l5x/lib/engine/acd-edit.d.ts +13 -0
  61. package/vendor/l5x/lib/engine/acd-edit.js +115 -0
  62. package/vendor/l5x/lib/engine/analysis.d.ts +22 -0
  63. package/vendor/l5x/lib/engine/analysis.js +258 -0
  64. package/vendor/l5x/lib/engine/capabilities.d.ts +9 -0
  65. package/vendor/l5x/lib/engine/capabilities.js +40 -0
  66. package/vendor/l5x/lib/engine/change-set.d.ts +122 -0
  67. package/vendor/l5x/lib/engine/change-set.js +446 -0
  68. package/vendor/l5x/lib/engine/configuration.d.ts +21 -0
  69. package/vendor/l5x/lib/engine/configuration.js +42 -0
  70. package/vendor/l5x/lib/engine/details.d.ts +47 -0
  71. package/vendor/l5x/lib/engine/details.js +173 -0
  72. package/vendor/l5x/lib/engine/direct-polling.d.ts +91 -0
  73. package/vendor/l5x/lib/engine/direct-polling.js +193 -0
  74. package/vendor/l5x/lib/engine/download-review.d.ts +15 -0
  75. package/vendor/l5x/lib/engine/download-review.js +63 -0
  76. package/vendor/l5x/lib/engine/logic-syntax.d.ts +60 -0
  77. package/vendor/l5x/lib/engine/logic-syntax.js +237 -0
  78. package/vendor/l5x/lib/engine/observations.d.ts +41 -0
  79. package/vendor/l5x/lib/engine/observations.js +67 -0
  80. package/vendor/l5x/lib/engine/project-analysis.d.ts +131 -0
  81. package/vendor/l5x/lib/engine/project-analysis.js +314 -0
  82. package/vendor/l5x/lib/engine/review-evidence.d.ts +8 -0
  83. package/vendor/l5x/lib/engine/review-evidence.js +49 -0
  84. package/vendor/l5x/lib/engine/review.d.ts +18 -0
  85. package/vendor/l5x/lib/engine/review.js +123 -0
  86. package/vendor/l5x/lib/engine/studio-sdk.d.ts +101 -0
  87. package/vendor/l5x/lib/engine/studio-sdk.js +571 -0
  88. package/vendor/l5x/lib/engine/tag-resolution.d.ts +20 -0
  89. package/vendor/l5x/lib/engine/tag-resolution.js +169 -0
  90. package/vendor/l5x/lib/engine/usage.d.ts +16 -0
  91. package/vendor/l5x/lib/engine/usage.js +92 -0
  92. package/vendor/l5x/lib/engine/xml-regions.d.ts +23 -0
  93. package/vendor/l5x/lib/engine/xml-regions.js +77 -0
  94. package/vendor/l5x/lib/fs.d.ts +17 -0
  95. package/vendor/l5x/lib/fs.js +36 -0
  96. package/vendor/l5x/lib/index.d.ts +36 -0
  97. package/vendor/l5x/lib/index.js +42 -0
  98. package/vendor/l5x/lib/live.d.ts +48 -0
  99. package/vendor/l5x/lib/live.js +48 -0
  100. package/vendor/l5x/lib/model/aois.d.ts +39 -0
  101. package/vendor/l5x/lib/model/aois.js +112 -0
  102. package/vendor/l5x/lib/model/errors.d.ts +15 -0
  103. package/vendor/l5x/lib/model/errors.js +18 -0
  104. package/vendor/l5x/lib/model/index.d.ts +20 -0
  105. package/vendor/l5x/lib/model/index.js +19 -0
  106. package/vendor/l5x/lib/model/module.d.ts +37 -0
  107. package/vendor/l5x/lib/model/module.js +54 -0
  108. package/vendor/l5x/lib/model/modules.d.ts +24 -0
  109. package/vendor/l5x/lib/model/modules.js +55 -0
  110. package/vendor/l5x/lib/model/parse.d.ts +32 -0
  111. package/vendor/l5x/lib/model/parse.js +116 -0
  112. package/vendor/l5x/lib/model/project.d.ts +15 -0
  113. package/vendor/l5x/lib/model/project.js +31 -0
  114. package/vendor/l5x/lib/model/routines.d.ts +71 -0
  115. package/vendor/l5x/lib/model/routines.js +183 -0
  116. package/vendor/l5x/lib/model/tags.d.ts +51 -0
  117. package/vendor/l5x/lib/model/tags.js +119 -0
  118. package/vendor/l5x/lib/model/types.d.ts +304 -0
  119. package/vendor/l5x/lib/model/types.js +12 -0
  120. package/vendor/l5x/lib/model/udts.d.ts +34 -0
  121. package/vendor/l5x/lib/model/udts.js +81 -0
  122. package/vendor/l5x/lib/model/xml.d.ts +139 -0
  123. package/vendor/l5x/lib/model/xml.js +291 -0
  124. package/vendor/l5x/lib/model/xref.d.ts +28 -0
  125. package/vendor/l5x/lib/model/xref.js +154 -0
  126. package/vendor/l5x/lib/session.d.ts +119 -0
  127. package/vendor/l5x/lib/session.js +236 -0
  128. package/vendor/l5x/lib/skill.d.ts +30 -0
  129. package/vendor/l5x/lib/skill.js +44 -0
  130. package/vendor/l5x/lib/studio-state.d.ts +77 -0
  131. package/vendor/l5x/lib/studio-state.js +79 -0
  132. package/vendor/l5x/lib/studio.d.ts +29 -0
  133. package/vendor/l5x/lib/studio.js +40 -0
  134. package/vendor/l5x/lib/tools/checks.d.ts +19 -0
  135. package/vendor/l5x/lib/tools/checks.js +140 -0
  136. package/vendor/l5x/lib/tools/common.d.ts +41 -0
  137. package/vendor/l5x/lib/tools/common.js +84 -0
  138. package/vendor/l5x/lib/tools/controller.d.ts +21 -0
  139. package/vendor/l5x/lib/tools/controller.js +170 -0
  140. package/vendor/l5x/lib/tools/drafts.d.ts +23 -0
  141. package/vendor/l5x/lib/tools/drafts.js +270 -0
  142. package/vendor/l5x/lib/tools/live.d.ts +21 -0
  143. package/vendor/l5x/lib/tools/live.js +175 -0
  144. package/vendor/l5x/lib/tools/read.d.ts +19 -0
  145. package/vendor/l5x/lib/tools/read.js +245 -0
  146. package/vendor/l5x/lib/tools/references.d.ts +20 -0
  147. package/vendor/l5x/lib/tools/references.js +269 -0
  148. package/vendor/l5x/lib/tools/studio.d.ts +21 -0
  149. package/vendor/l5x/lib/tools/studio.js +193 -0
  150. package/vendor/l5x/poll-worker.mjs +77 -0
  151. package/vendor/l5x/studio-bridge/NuGet.Config +1 -0
  152. package/vendor/l5x/studio-bridge/Program.cs +304 -0
  153. package/vendor/l5x/studio-bridge/StudioBridge.csproj +11 -0
  154. package/vendor/mwi/README.md +60 -0
  155. package/vendor/mwi/SKILL.md +87 -0
  156. package/vendor/mwi/bridge/README.md +8 -0
  157. package/vendor/mwi/bridge/mw_bridge.ps1 +2763 -0
  158. package/vendor/mwi/bridge/sdk-interop-importer.cs +30 -0
  159. package/vendor/mwi/bridge/sdk-variable-reader.cs +22 -0
  160. package/vendor/mwi/bridge/start_bridge.cmd +21 -0
  161. package/vendor/mwi/bridge/watch_dialogs.cmd +13 -0
  162. package/vendor/mwi/bridge/watch_dialogs.ps1 +153 -0
  163. package/vendor/mwi/code/engine/motionworks_iec_mcp/__init__.py +14 -0
  164. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/__init__.cpython-312.pyc +0 -0
  165. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/block_interfaces.cpython-312.pyc +0 -0
  166. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/cfb.cpython-312.pyc +0 -0
  167. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/datatypes.cpython-312.pyc +0 -0
  168. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/declarations.cpython-312.pyc +0 -0
  169. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/errors.cpython-312.pyc +0 -0
  170. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/graphical_listing.cpython-312.pyc +0 -0
  171. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/grid.cpython-312.pyc +0 -0
  172. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/knowledge.cpython-312.pyc +0 -0
  173. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/mwt_bind.cpython-312.pyc +0 -0
  174. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/navigation.cpython-312.pyc +0 -0
  175. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/program_checks.cpython-312.pyc +0 -0
  176. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/project.cpython-312.pyc +0 -0
  177. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/staging.cpython-312.pyc +0 -0
  178. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/structure.cpython-312.pyc +0 -0
  179. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/task_bindings.cpython-312.pyc +0 -0
  180. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tasks.cpython-312.pyc +0 -0
  181. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/tree.cpython-312.pyc +0 -0
  182. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/validation.cpython-312.pyc +0 -0
  183. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variable_edit.cpython-312.pyc +0 -0
  184. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/variables.cpython-312.pyc +0 -0
  185. package/vendor/mwi/code/engine/motionworks_iec_mcp/__pycache__/workflow.cpython-312.pyc +0 -0
  186. package/vendor/mwi/code/engine/motionworks_iec_mcp/block_interfaces.py +275 -0
  187. package/vendor/mwi/code/engine/motionworks_iec_mcp/build.py +339 -0
  188. package/vendor/mwi/code/engine/motionworks_iec_mcp/capture.py +448 -0
  189. package/vendor/mwi/code/engine/motionworks_iec_mcp/cfb.py +765 -0
  190. package/vendor/mwi/code/engine/motionworks_iec_mcp/com.py +268 -0
  191. package/vendor/mwi/code/engine/motionworks_iec_mcp/datatypes.py +204 -0
  192. package/vendor/mwi/code/engine/motionworks_iec_mcp/declarations.py +399 -0
  193. package/vendor/mwi/code/engine/motionworks_iec_mcp/descriptions.py +30 -0
  194. package/vendor/mwi/code/engine/motionworks_iec_mcp/eip.py +292 -0
  195. package/vendor/mwi/code/engine/motionworks_iec_mcp/errors.py +36 -0
  196. package/vendor/mwi/code/engine/motionworks_iec_mcp/graphical_listing.py +208 -0
  197. package/vendor/mwi/code/engine/motionworks_iec_mcp/grid.py +227 -0
  198. package/vendor/mwi/code/engine/motionworks_iec_mcp/ide.py +317 -0
  199. package/vendor/mwi/code/engine/motionworks_iec_mcp/iec.py +317 -0
  200. package/vendor/mwi/code/engine/motionworks_iec_mcp/installed_help.py +151 -0
  201. package/vendor/mwi/code/engine/motionworks_iec_mcp/knowledge.py +218 -0
  202. package/vendor/mwi/code/engine/motionworks_iec_mcp/libraries.py +250 -0
  203. package/vendor/mwi/code/engine/motionworks_iec_mcp/manuals.py +520 -0
  204. package/vendor/mwi/code/engine/motionworks_iec_mcp/mwt_bind.py +133 -0
  205. package/vendor/mwi/code/engine/motionworks_iec_mcp/navigation.py +44 -0
  206. package/vendor/mwi/code/engine/motionworks_iec_mcp/pou_writer.py +1547 -0
  207. package/vendor/mwi/code/engine/motionworks_iec_mcp/program_checks.py +360 -0
  208. package/vendor/mwi/code/engine/motionworks_iec_mcp/project.py +704 -0
  209. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/catalog.json +672 -0
  210. package/vendor/mwi/code/engine/motionworks_iec_mcp/reference_data/patterns.json +86 -0
  211. package/vendor/mwi/code/engine/motionworks_iec_mcp/restore.py +208 -0
  212. package/vendor/mwi/code/engine/motionworks_iec_mcp/server.py +1913 -0
  213. package/vendor/mwi/code/engine/motionworks_iec_mcp/snapshot.py +138 -0
  214. package/vendor/mwi/code/engine/motionworks_iec_mcp/st_comments.py +25 -0
  215. package/vendor/mwi/code/engine/motionworks_iec_mcp/staging.py +179 -0
  216. package/vendor/mwi/code/engine/motionworks_iec_mcp/stlint.py +676 -0
  217. package/vendor/mwi/code/engine/motionworks_iec_mcp/structure.py +98 -0
  218. package/vendor/mwi/code/engine/motionworks_iec_mcp/task_bindings.py +68 -0
  219. package/vendor/mwi/code/engine/motionworks_iec_mcp/tasks.py +162 -0
  220. package/vendor/mwi/code/engine/motionworks_iec_mcp/transaction.py +157 -0
  221. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree.py +563 -0
  222. package/vendor/mwi/code/engine/motionworks_iec_mcp/tree_writer.py +790 -0
  223. package/vendor/mwi/code/engine/motionworks_iec_mcp/uia.py +465 -0
  224. package/vendor/mwi/code/engine/motionworks_iec_mcp/validation.py +41 -0
  225. package/vendor/mwi/code/engine/motionworks_iec_mcp/variable_edit.py +183 -0
  226. package/vendor/mwi/code/engine/motionworks_iec_mcp/variables.py +472 -0
  227. package/vendor/mwi/code/engine/motionworks_iec_mcp/workflow.py +130 -0
  228. package/vendor/mwi/code/engine/motionworks_iec_mcp/writer.py +666 -0
  229. package/vendor/mwi/code/mw_code.py +1258 -0
  230. package/vendor/mwi/legacy/edit-session.js +50 -0
  231. package/vendor/mwi/legacy/fb-insertion.js +74 -0
  232. package/vendor/mwi/legacy/native-code.js +83 -0
  233. package/vendor/mwi/legacy/native-groups.js +75 -0
  234. package/vendor/mwi/legacy/native-structure.js +143 -0
  235. package/vendor/mwi/legacy/native-variables.js +77 -0
  236. package/vendor/mwi/legacy/pou-conversion.js +47 -0
  237. package/vendor/mwi/legacy/pou-package.js +66 -0
  238. package/vendor/mwi/lib/core/bridge.d.ts +71 -0
  239. package/vendor/mwi/lib/core/bridge.js +53 -0
  240. package/vendor/mwi/lib/core/code-engine.d.ts +47 -0
  241. package/vendor/mwi/lib/core/code-engine.js +109 -0
  242. package/vendor/mwi/lib/core/errors.d.ts +37 -0
  243. package/vendor/mwi/lib/core/errors.js +47 -0
  244. package/vendor/mwi/lib/core/params.d.ts +52 -0
  245. package/vendor/mwi/lib/core/params.js +46 -0
  246. package/vendor/mwi/lib/core/ps-bridge.d.ts +62 -0
  247. package/vendor/mwi/lib/core/ps-bridge.js +127 -0
  248. package/vendor/mwi/lib/core/result.d.ts +45 -0
  249. package/vendor/mwi/lib/core/result.js +20 -0
  250. package/vendor/mwi/lib/core/tool.d.ts +80 -0
  251. package/vendor/mwi/lib/core/tool.js +55 -0
  252. package/vendor/mwi/lib/edit/compare.d.ts +33 -0
  253. package/vendor/mwi/lib/edit/compare.js +70 -0
  254. package/vendor/mwi/lib/edit.d.ts +20 -0
  255. package/vendor/mwi/lib/edit.js +32 -0
  256. package/vendor/mwi/lib/index.d.ts +21 -0
  257. package/vendor/mwi/lib/index.js +33 -0
  258. package/vendor/mwi/lib/read/service.d.ts +117 -0
  259. package/vendor/mwi/lib/read/service.js +229 -0
  260. package/vendor/mwi/lib/read.d.ts +21 -0
  261. package/vendor/mwi/lib/read.js +33 -0
  262. package/vendor/mwi/lib/session/project.d.ts +34 -0
  263. package/vendor/mwi/lib/session/project.js +54 -0
  264. package/vendor/mwi/lib/session/service.d.ts +167 -0
  265. package/vendor/mwi/lib/session/service.js +566 -0
  266. package/vendor/mwi/lib/session/stage.d.ts +84 -0
  267. package/vendor/mwi/lib/session/stage.js +187 -0
  268. package/vendor/mwi/lib/session/workspace.d.ts +52 -0
  269. package/vendor/mwi/lib/session/workspace.js +90 -0
  270. package/vendor/mwi/lib/skill.d.ts +36 -0
  271. package/vendor/mwi/lib/skill.js +56 -0
  272. package/vendor/mwi/lib/tools/edit.d.ts +20 -0
  273. package/vendor/mwi/lib/tools/edit.js +286 -0
  274. package/vendor/mwi/lib/tools/read.d.ts +19 -0
  275. package/vendor/mwi/lib/tools/read.js +161 -0
  276. package/vendor/mwi/lib/tools/session.d.ts +20 -0
  277. package/vendor/mwi/lib/tools/session.js +166 -0
  278. package/vendor/mwi/lib/tools/verify.d.ts +19 -0
  279. package/vendor/mwi/lib/tools/verify.js +85 -0
  280. package/vendor/mwi/lib/verify.d.ts +17 -0
  281. package/vendor/mwi/lib/verify.js +29 -0
@@ -0,0 +1,30 @@
1
+ using System; using System.IO; using System.Reflection; using System.Reflection.Emit;
2
+ using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes;
3
+ using System.Collections.Generic;
4
+ public class MwSdkInteropImporter : ITypeLibImporterNotifySink {
5
+ [DllImport("oleaut32.dll",CharSet=CharSet.Unicode)] static extern int LoadTypeLibEx(string file,int kind,out ITypeLib lib);
6
+ readonly string directory; readonly Dictionary<string,Assembly> imports=new Dictionary<string,Assembly>();
7
+ public readonly List<string> Notices=new List<string>();
8
+ public MwSdkInteropImporter(string dir){directory=dir;}
9
+ public void ReportEvent(ImporterEventKind kind,int code,string text){if(kind!=ImporterEventKind.NOTIF_TYPECONVERTED)Notices.Add(kind+":"+code+":"+text);}
10
+ public Assembly ResolveRef(object lib){throw new InvalidOperationException("Untested SDK assembly reference");}
11
+ public Assembly Import(ITypeLib lib,string ns){
12
+ string name,doc,helpFile;int help;lib.GetDocumentation(-1,out name,out doc,out help,out helpFile);
13
+ name=System.Text.RegularExpressions.Regex.Replace(name,"[^A-Za-z0-9_]","_");
14
+ Assembly found;if(imports.TryGetValue(name,out found))return found;
15
+ var file=Path.Combine(directory,name+".dll");
16
+ if(File.Exists(file))throw new InvalidOperationException("Refusing existing generated assembly "+file);
17
+ var assembly=new TypeLibConverter().ConvertTypeLibToAssembly(lib,file,TypeLibImporterFlags.ImportAsX86,this,null,null,ns,null);
18
+ assembly.Save(Path.GetFileName(file));imports.Add(name,assembly);return assembly;
19
+ }
20
+ public static string[] Generate(string tlb,string directory){
21
+ ITypeLib lib;Marshal.ThrowExceptionForHR(LoadTypeLibEx(tlb,2,out lib));
22
+ var importer=new MwSdkInteropImporter(directory);var assembly=importer.Import(lib,"MwAdeGenerated");
23
+ var results=new List<string>();
24
+ foreach(var type in assembly.GetTypes())if(type.Name=="_Variables"||type.Name=="_Variable"){
25
+ results.Add(type.FullName+" GUID="+type.GUID);
26
+ foreach(var method in type.GetMethods())results.Add(method.ToString());
27
+ }
28
+ results.AddRange(importer.Notices);return results.ToArray();
29
+ }
30
+ }
@@ -0,0 +1,22 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using MwAdeGenerated;
4
+ public static class MwSdkVariableReader {
5
+ static object Optional(string value){return String.IsNullOrEmpty(value)?null:value;}
6
+ public static Dictionary<string,object>[] Read(object source){
7
+ var vars=(_Variables)source;int count=vars.Count;
8
+ var result=new List<Dictionary<string,object>>();
9
+ var sections=new Dictionary<int,string>{{1,"VAR"},{2,"VAR_INPUT"},{3,"VAR_OUTPUT"},{4,"VAR_IN_OUT"},{5,"VAR_EXTERNAL"},{6,"VAR_GLOBAL"}};
10
+ for(int n=1;n<=count;n++){
11
+ var v=(_Variable)vars.Item(n);string section;
12
+ if(!sections.TryGetValue((int)v.BlockType,out section))throw new InvalidOperationException("Unsupported native declaration usage");
13
+ result.Add(new Dictionary<string,object>{
14
+ {"name",v.Name},{"type",v.DataType},{"section",section},{"group",v.Group.Name},
15
+ {"address",Optional(v.IecAddress)},{"initial_value",Optional(v.InitialValue)},{"description",Optional(v.Comment)},
16
+ {"retain",v.Retain},{"pdd",v.PDD},{"opc",v.OPC},{"disabled",v.Disabled},{"not_on_plc",v.NotOnPlc},{"redundant",v.Redundant}
17
+ });
18
+ }
19
+ if(vars.Count!=count)throw new InvalidOperationException("Native declaration count changed during inspection");
20
+ return result.ToArray();
21
+ }
22
+ }
@@ -0,0 +1,21 @@
1
+ @echo off
2
+ rem Launches the MotionWorks Use COM bridge under 32-bit Windows PowerShell.
3
+ rem
4
+ rem WHY A .cmd WRAPPER
5
+ rem ------------------
6
+ rem Mwt.exe is a 32-bit LocalServer32, so a 64-bit client cannot create its COM
7
+ rem objects at all - the bitness here is load-bearing.
8
+ rem
9
+ rem The plugin spawns THIS file rather than invoking powershell.exe directly,
10
+ rem because Node's Windows argument quoting mangles the
11
+ rem -Command "& '<path>'"
12
+ rem form: the child starts and exits 0 without ever running the script. Passing a
13
+ rem single launcher path to cmd.exe is unambiguous.
14
+ rem
15
+ rem %~dp0 already ends with a backslash, so '%~dp0mw_bridge.ps1' resolves to the
16
+ rem sibling script.
17
+ rem
18
+ rem -WindowStyle Hidden matters: without it, powershell.exe creates its OWN
19
+ rem visible console window. That console then sits on top of the IDE, and because
20
+ rem mw_ide_screenshot captures a screen region it appeared inside the capture.
21
+ "%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command "& '%~dp0mw_bridge.ps1'"
@@ -0,0 +1,13 @@
1
+ @echo off
2
+ rem Launches the MotionWorks Use modal-dialog watcher, hidden and detached.
3
+ rem
4
+ rem The bridge starts this immediately before OpenProject, because that COM call
5
+ rem blocks for as long as MotionWorks keeps a prompt on screen - so the prompt can
6
+ rem only be answered from another process. It is killed once OpenProject returns
7
+ rem or the bridge gives up.
8
+ rem
9
+ rem Takes the lifetime in seconds as %1, so the bridge controls the budget.
10
+ rem
11
+ rem -WindowStyle Hidden for the same reason start_bridge.cmd needs it: a visible
12
+ rem console would sit on top of the IDE and end up inside any screenshot.
13
+ "%SystemRoot%\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -Command "& '%~dp0watch_dialogs.ps1' -Seconds %1"
@@ -0,0 +1,153 @@
1
+ <#
2
+ MotionWorks Use - modal-dialog watcher.
3
+
4
+ WHY THIS IS A SEPARATE PROCESS
5
+ ------------------------------
6
+ OpenProject is a synchronous out-of-process COM call. When MotionWorks raises a
7
+ prompt *during* the load - "this project was not closed cleanly, load anyway?",
8
+ "defragment?" - the server enters a modal loop and the call does not return until
9
+ that prompt is answered. The bridge thread is therefore BLOCKED by definition and
10
+ cannot answer it; no retry, no timeout, no catch. Measured: an open call sat for
11
+ minutes with the request logged and no response.
12
+
13
+ So the answering has to happen on another thread, and in PowerShell that means
14
+ another process. The bridge starts this watcher immediately before OpenProject and
15
+ kills it afterwards; in between it presses the obvious answer on any known prompt.
16
+
17
+ SAFETY
18
+ ------
19
+ Only ever touches prompts raised by the MotionWorks IDE process, and only presses
20
+ buttons from a fixed list of obviously-safe answers about a STAGED COPY - the
21
+ plugin never opens the user's original project. Anything unrecognised is left
22
+ completely alone.
23
+ #>
24
+ param(
25
+ [int]$Seconds = 150,
26
+ [int]$PollMs = 400
27
+ )
28
+
29
+ $ErrorActionPreference = 'Continue'
30
+
31
+ Add-Type -TypeDefinition @'
32
+ using System; using System.Text; using System.Runtime.InteropServices;
33
+ public class WDW {
34
+ [DllImport("user32.dll")] public static extern bool EnumWindows(EnumWindowsProc cb, IntPtr l);
35
+ [DllImport("user32.dll")] public static extern bool EnumChildWindows(IntPtr p, EnumWindowsProc cb, IntPtr l);
36
+ public delegate bool EnumWindowsProc(IntPtr h, IntPtr l);
37
+ [DllImport("user32.dll",CharSet=CharSet.Unicode)] public static extern int GetClassNameW(IntPtr h, StringBuilder s, int n);
38
+ [DllImport("user32.dll",CharSet=CharSet.Unicode)] public static extern int GetWindowTextW(IntPtr h, StringBuilder s, int n);
39
+ [DllImport("user32.dll")] public static extern bool IsWindowVisible(IntPtr h);
40
+ [DllImport("user32.dll")] public static extern int GetDlgCtrlID(IntPtr h);
41
+ [DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint p);
42
+ [DllImport("user32.dll", EntryPoint="SendMessageTimeoutW", CharSet=CharSet.Unicode)]
43
+ public static extern IntPtr SendTimeoutStr(IntPtr h, uint msg, IntPtr wp, StringBuilder lp, uint flags, uint ms, out IntPtr res);
44
+ [DllImport("user32.dll", EntryPoint="SendMessageTimeoutW", CharSet=CharSet.Unicode)]
45
+ public static extern IntPtr SendTimeoutInt(IntPtr h, uint msg, IntPtr wp, IntPtr lp, uint flags, uint ms, out IntPtr res);
46
+
47
+ const uint SMTO_ABORTIFHUNG = 0x0002;
48
+
49
+ public static string ReadText(IntPtr h) {
50
+ IntPtr r;
51
+ if (SendTimeoutInt(h, 0x000E, IntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 150, out r) == IntPtr.Zero) return "";
52
+ int n = r.ToInt32(); if (n <= 0) return ""; if (n > 32768) n = 32768;
53
+ StringBuilder sb = new StringBuilder(n + 2);
54
+ if (SendTimeoutStr(h, 0x000D, (IntPtr)(n + 1), sb, SMTO_ABORTIFHUNG, 150, out r) == IntPtr.Zero) return "";
55
+ return sb.ToString();
56
+ }
57
+
58
+ public static bool Click(IntPtr h) {
59
+ IntPtr r;
60
+ return SendTimeoutInt(h, 0x00F5, IntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 1500, out r) != IntPtr.Zero;
61
+ }
62
+ }
63
+ '@
64
+
65
+ # Prompts with one obviously-safe answer. These are questions about a staged COPY,
66
+ # so the answer cannot endanger real work. Anything absent from this table is left
67
+ # strictly alone - the watcher never guesses.
68
+ $known = @(
69
+ @{ match = 'defragment'; button = 'No' },
70
+ @{ match = 'would you like to load the project anyway'; button = 'Yes' },
71
+ @{ match = 'currently loaded by'; button = 'Yes' },
72
+ @{ match = 'abnormal termination'; button = 'Yes' },
73
+ @{ match = 'disabled/enabled by software key'; button = 'OK' },
74
+ @{ match = 'project is currently loaded'; button = 'Yes' }
75
+ )
76
+
77
+ $deadline = (Get-Date).AddSeconds($Seconds)
78
+ $answeredCount = 0
79
+
80
+ while ((Get-Date) -lt $deadline) {
81
+ Start-Sleep -Milliseconds $PollMs
82
+
83
+ # IDE pid, taken from the IDE window (never from a process-name lookup).
84
+ $script:idePid = 0
85
+ $cbIde = [WDW+EnumWindowsProc]{
86
+ param($h, $l)
87
+ $t = New-Object System.Text.StringBuilder 256
88
+ [void][WDW]::GetWindowTextW($h, $t, 256)
89
+ $title = $t.ToString()
90
+ if ([WDW]::IsWindowVisible($h) -and ($title -like 'MotionWorks IEC 3 Pro*' -or $title -like 'MULTIPROG*')) {
91
+ $p = [uint32]0; [void][WDW]::GetWindowThreadProcessId($h, [ref]$p)
92
+ $script:idePid = [int]$p
93
+ }
94
+ return $true
95
+ }
96
+ [void][WDW]::EnumWindows($cbIde, [IntPtr]::Zero)
97
+ if ($script:idePid -eq 0) { continue }
98
+
99
+ $script:hits = New-Object System.Collections.ArrayList
100
+ $cbDlg = [WDW+EnumWindowsProc]{
101
+ param($h, $l)
102
+ if (-not [WDW]::IsWindowVisible($h)) { return $true }
103
+ $p = [uint32]0; [void][WDW]::GetWindowThreadProcessId($h, [ref]$p)
104
+ if ([int]$p -ne $script:idePid) { return $true }
105
+ $c = New-Object System.Text.StringBuilder 64
106
+ [void][WDW]::GetClassNameW($h, $c, 64)
107
+ if ($c.ToString() -ne '#32770') { return $true }
108
+
109
+ $script:btnHandles = @{}
110
+ $texts = New-Object System.Collections.ArrayList
111
+ $cbKid = [WDW+EnumWindowsProc]{
112
+ param($k, $l2)
113
+ if (-not [WDW]::IsWindowVisible($k)) { return $true }
114
+ $kc = New-Object System.Text.StringBuilder 64
115
+ [void][WDW]::GetClassNameW($k, $kc, 64)
116
+ $cn = $kc.ToString()
117
+ if ($cn -ne 'Button' -and $cn -ne 'Static') { return $true }
118
+ $txt = [WDW]::ReadText($k)
119
+ if (-not $txt) { return $true }
120
+ if ($cn -eq 'Button') {
121
+ # Strip the accelerator marker to get the plain label. NB: a named
122
+ # regex group does NOT become a variable in PowerShell - it lives in
123
+ # $Matches - so the earlier `-match '^(?<lbl>.+)$'` form silently
124
+ # keyed every button on the empty string, the lookup never matched,
125
+ # and the watcher sat there answering nothing while the IDE stayed
126
+ # blocked. Verified by instrumenting it: message read fine, buttons
127
+ # dictionary empty.
128
+ $label = ($txt.Trim() -replace '&', '')
129
+ if ($label) { $script:btnHandles[$label] = $k }
130
+ } else { [void]$texts.Add($txt.Trim()) }
131
+ return $true
132
+ }
133
+ [void][WDW]::EnumChildWindows($h, $cbKid, [IntPtr]::Zero)
134
+ [void]$script:hits.Add([pscustomobject]@{
135
+ HWND = $h
136
+ Message = (($texts | Select-Object -Unique) -join "`n")
137
+ Buttons = $script:btnHandles
138
+ })
139
+ return $true
140
+ }
141
+ [void][WDW]::EnumWindows($cbDlg, [IntPtr]::Zero)
142
+
143
+ foreach ($d in $script:hits) {
144
+ $hay = $d.Message.ToLower()
145
+ foreach ($k in $known) {
146
+ if ($hay -notlike "*$($k.match)*") { continue }
147
+ $target = $null
148
+ foreach ($lbl in $d.Buttons.Keys) { if ($lbl -ieq $k.button) { $target = $d.Buttons[$lbl]; break } }
149
+ if ($target -and [WDW]::Click($target)) { $answeredCount++ }
150
+ break
151
+ }
152
+ }
153
+ }
@@ -0,0 +1,14 @@
1
+ """File engine behind the MotionWorks plugin.
2
+
3
+ The package is layered so that the dangerous parts stay in one place:
4
+
5
+ cfb - OLE/CFB reader + writer primitives (no MotionWorks knowledge)
6
+ project - project/expanded-directory resolution and inventory
7
+ variables - merged view of the .VB text and .VGR binary variable stores
8
+ errors - typed failures that surface as readable tool errors
9
+ staging - refuses anything that is not a proven staged copy
10
+
11
+ The plugin in index.js is the tool surface. Writes go through mw_code.py.
12
+ """
13
+
14
+ __version__ = "0.1.0"
@@ -0,0 +1,275 @@
1
+ """Read block interfaces from the project's bound, installed libraries.
2
+
3
+ Firmware .PT parameter tables explicitly encode direction/type. User library
4
+ interfaces come from declaration streams, never from compiled identifier heaps.
5
+ Unsupported worksheets can expose independently counted, explicit cache
6
+ declarations as diagnostic evidence only, with insertion eligibility false.
7
+ This module only reads; no library is opened for editing or copied into a POU.
8
+ """
9
+ import hashlib
10
+ import re
11
+ from pathlib import Path
12
+ from .cfb import CompoundFile
13
+ from .project import Project
14
+ from .tree import parse_document
15
+ from .variables import decode_declarations,read_grid_variable_count,cross_check,parse_declarations,CompressedStreamError
16
+ from .graphical_listing import DEPENDENCY_DECLARATION,identity as compiler_identity
17
+
18
+ SECTIONS={'VAR_INPUT':'input','VAR_OUTPUT':'output','VAR_IN_OUT':'in_out'}
19
+ KINDS={'FUNCTION':'FUNCTION','FUNCTION_BLOCK':'FUNCTION_BLOCK','FUNCTIONBLOCK':'FUNCTION_BLOCK','PROGRAM':'PROGRAM'}
20
+ IDENTIFIER=re.compile(r'^[A-Za-z_][A-Za-z_0-9]*$')
21
+
22
+ def compiler_type_names(text):
23
+ """Read explicit names/type IDs from the observed complete TYLLIST table."""
24
+ header=re.match(r'\A\(\*\r?\nNDTE: (\d+)\r?\nNCPE: (\d+)\r?\nNDME: (\d+)\r?\n\*\)\r?\n',text)
25
+ if not header or '\0' in text:raise ValueError('Unsupported compiler type-table header/encoding')
26
+ roots=[];properties=0;dimensions=0;current=None;children=0;types={};root_ids=set();root_names=set()
27
+ def bind(name,number):
28
+ if not IDENTIFIER.fullmatch(name) or not number.isdigit() or int(number)<1:raise ValueError('Invalid compiler type identity')
29
+ key=int(number)
30
+ if key in types and types[key].casefold()!=name.casefold():raise ValueError('Conflicting compiler type names')
31
+ types[key]=name
32
+ def finish():
33
+ if current and current[6]!='STRING' and children!=int(current[4]):raise ValueError('Compiler type member count mismatch')
34
+ for line in text[header.end():].splitlines():
35
+ if not line:continue
36
+ fields=line.split('\t')
37
+ if len(fields)<2:raise ValueError('Unsupported compiler type row')
38
+ if not re.fullmatch(r'\d+ \d+',fields[0]):raise ValueError('Invalid compiler type source identity')
39
+ if len(fields)>6 and fields[1]:
40
+ finish()
41
+ if len(fields) not in (11,12) or fields[5]!='USER' or fields[6] not in ('STRUCT','ARRAY','ENUM','STRING') or not fields[4].isdigit():raise ValueError('Unsupported compiler type root')
42
+ if fields[3] in root_ids or fields[2].casefold() in root_names:raise ValueError('Duplicate compiler type root')
43
+ root_ids.add(fields[3]);root_names.add(fields[2].casefold());bind(fields[2],fields[3])
44
+ if fields[6]=='ARRAY':bind(fields[7],fields[8])
45
+ roots.append(fields);current=fields;children=0
46
+ else:
47
+ if current is None or fields[1] or len(fields) not in (6,7):raise ValueError('Unsupported compiler type member')
48
+ children+=1
49
+ if current[6]=='STRUCT':
50
+ if not IDENTIFIER.fullmatch(fields[2]):raise ValueError('Invalid compiler structure member')
51
+ bind(fields[3],fields[4]);properties+=1
52
+ elif current[6]=='ARRAY':
53
+ if fields[2] or not re.fullmatch(r'-?\d+',fields[3]) or not re.fullmatch(r'-?\d+',fields[4]):raise ValueError('Invalid compiler array dimension')
54
+ dimensions+=1
55
+ elif current[6]=='ENUM':
56
+ if not IDENTIFIER.fullmatch(fields[2]):raise ValueError('Invalid compiler enum member')
57
+ properties+=1
58
+ else:raise ValueError('Unexpected compiler string member')
59
+ finish()
60
+ if (len(roots),properties,dimensions)!=tuple(int(x) for x in header.groups()):raise ValueError('Compiler type table is incomplete')
61
+ return types
62
+
63
+ def parse_compiled_interface(listing,dependency,name,worksheet,type_names=None):
64
+ """Diagnostic pins from explicit cache declarations, independently counted.
65
+
66
+ This does not establish freshness against a protected source worksheet and
67
+ intentionally has a different evidence kind from insertion-eligible tables.
68
+ """
69
+ if '\0' in listing or '\0' in dependency:raise ValueError('Unsupported compiled interface encoding')
70
+ listing=listing.replace('\r','')
71
+ header=re.match(r'\A\(\*\n(.*?)\n\*\)\nFUNCTION_BLOCK ([A-Za-z_][A-Za-z_0-9]*)\n',listing,re.S)
72
+ if not header or header[2]!=name:raise ValueError('Compiled library interface identity mismatch')
73
+ counts=re.findall(r'^NVD:\s*(\d+)\s*$',header[1],re.M)
74
+ kinds=re.findall(r'^T:\s*(\w+)\s*$',header[1],re.M)
75
+ if kinds!=['FUNCTION_BLOCK'] or len(counts)!=1:raise ValueError('Compiled library header absent or ambiguous')
76
+ markers=list(re.finditer(r'^@WS (.+)$',listing,re.M))
77
+ expected='POE/'+name+'/'+worksheet
78
+ if len(markers)<2 or markers[0][1].replace('\\','/').casefold()!=expected.casefold():
79
+ raise ValueError('Compiled declaration worksheet identity mismatch')
80
+ declaration=listing[markers[0].end():markers[1].start()]
81
+ handles=re.findall(r'^@V (\d+) (\d+)\s+',declaration,re.M)
82
+ if not handles or len(set(handles))!=len(handles):raise ValueError('Compiled source declaration handles absent or duplicate')
83
+ cleaned=re.sub(r'^@V \d+ \d+\s*','',declaration,flags=re.M)
84
+ table=parse_declarations(cleaned)
85
+ if table.warnings or len(table.variables)!=len(handles):raise ValueError('Compiled source declarations incomplete')
86
+ names=[v.name.casefold() for v in table.variables]
87
+ if len(set(names))!=len(names):raise ValueError('Duplicate compiled source declaration name')
88
+ if compiler_identity(dependency)!=('FUNCTION_BLOCK',name):raise ValueError('Compiler dependency block identity mismatch')
89
+ dep_header=dependency.split('*)',1)[0]
90
+ dep_counts=re.findall(r'^QVE:\s*(\d+)\s*$',dep_header,re.M)
91
+ par_counts=re.findall(r'^QPar:\s*(\d+)\s*$',dep_header,re.M)
92
+ rows=list(DEPENDENCY_DECLARATION.finditer(dependency))
93
+ if len(dep_counts)!=1 or int(dep_counts[0])!=int(counts[0]) or len(rows)!=int(counts[0]):raise ValueError('Compiler declaration count mismatch')
94
+ if {int(r[2]) for r in rows}!=set(range(1,len(rows)+1)) or len({r[1].casefold() for r in rows})!=len(rows):
95
+ raise ValueError('Compiler declaration ordinals/names incomplete or duplicate')
96
+ source={(v.name,v.section) for v in table.variables}
97
+ ordinary={(r[1],r[3]) for r in rows if not re.fullmatch(r'@T_Code_\d+',r[1])}
98
+ if source!=ordinary or any(r[3]!='VAR' for r in rows if r[1].startswith('@')):
99
+ raise ValueError('Library/dependency declaration identities differ')
100
+ pins=[dict(name=v.name,type=v.type_name,section=v.section,direction=SECTIONS[v.section],initial_value=v.initial_value,description=v.description)
101
+ for v in table.variables if v.section in SECTIONS]
102
+ if par_counts!=[str(len(pins))] or not pins:raise ValueError('Compiler public parameter count mismatch')
103
+ if type_names is not None:
104
+ by_name={r[1]:r for r in rows}
105
+ for pin in pins:
106
+ token=re.fullmatch(r'@TYP:(\d+)\s*',by_name[pin['name']][4])
107
+ if not token or type_names.get(int(token[1]),'').casefold()!=pin['type'].casefold():raise ValueError('Compiled public pin type differs from explicit declaration')
108
+ return dict(pins=pins,source_declaration_count=len(table.variables),compiler_declaration_count=len(rows),compiler_pin_types_verified=type_names is not None)
109
+
110
+ def _cached_library_interface(project_root,library_root,pou):
111
+ cache=pou.source_path.parent/'tmp.sto'
112
+ if not cache.resolve().is_relative_to(Path(library_root).resolve()):raise ValueError('Linked library cache escapes its bound root')
113
+ before=hashlib.sha256(cache.read_bytes()).hexdigest()
114
+ source_hash=hashlib.sha256(pou.source_path.read_bytes()).hexdigest()
115
+ container=CompoundFile(cache);stream='@$@$@$@$.clu'
116
+ matches=[n for n in container.stream_names() if n.casefold()==stream.casefold()]
117
+ worksheets=[n for n in pou.source().stream_names() if n.upper().endswith('.VB')]
118
+ if len(matches)!=1 or len(worksheets)!=1:raise ValueError('Compiled library declaration stream absent or ambiguous')
119
+ listing=container.read_stream(matches[0])
120
+ if len(listing)>2_000_000:raise ValueError('Compiled declaration cache exceeds bound')
121
+ dependencies=[]
122
+ for path in (Path(project_root)/'C').rglob('ICI*.DIT'):
123
+ if not path.resolve().is_relative_to(Path(project_root).resolve()):raise ValueError('Linked compiler dependency escapes project')
124
+ if path.stat().st_size>2_000_000:raise ValueError('Compiler dependency exceeds bound')
125
+ raw=path.read_bytes()
126
+ try:kind,name=compiler_identity(raw.decode('latin1'))
127
+ except ValueError:continue
128
+ if kind=='FUNCTION_BLOCK' and name==pou.name:dependencies.append((path,raw))
129
+ if len(dependencies)!=1:raise ValueError('Matching saved compiler dependency absent or ambiguous')
130
+ dependency,raw=dependencies[0]
131
+ type_table=dependency.with_name('TYLLIST.TYP')
132
+ if not type_table.resolve().is_relative_to(Path(project_root).resolve()) or type_table.stat().st_size>2_000_000:raise ValueError('Compiler type table outside project or oversized')
133
+ type_bytes=type_table.read_bytes();type_names=compiler_type_names(type_bytes.decode('latin1'))
134
+ identifiers=re.findall(r'^CI#:\s*(\d+)\s*$',raw.decode('latin1').split('*)',1)[0],re.M)
135
+ if len(identifiers)!=1 or int(identifiers[0])!=int(dependency.stem[3:]):raise ValueError('Compiler dependency file/type identity mismatch')
136
+ result=parse_compiled_interface(listing.decode('latin1'),raw.decode('latin1'),pou.name,worksheets[0],type_names)
137
+ if before!=hashlib.sha256(cache.read_bytes()).hexdigest() or raw!=dependency.read_bytes() or type_bytes!=type_table.read_bytes() or source_hash!=hashlib.sha256(pou.source_path.read_bytes()).hexdigest():raise ValueError('Compiled interface changed during read')
138
+ result.update(source_stream=matches[0],cache_file=str(cache),cache_sha256=before,
139
+ worksheet_file=str(pou.source_path),worksheet_sha256=source_hash,
140
+ compiler_dependency=str(dependency),compiler_dependency_sha256=hashlib.sha256(raw).hexdigest(),
141
+ compiler_type_table=str(type_table),compiler_type_table_sha256=hashlib.sha256(type_bytes).hexdigest(),
142
+ origin='user_library_compiled_declarations',compiler_cache_freshness_verified=False,compiler_source_binding_verified=False,
143
+ insertion_eligible=False,evidence_kind='installed-compiled-block-interface')
144
+ return result
145
+
146
+ def parse_parameter_table(data):
147
+ headers={}; pins=[]; started=False
148
+ for line in data.decode('latin1').splitlines():
149
+ if not line.strip():continue
150
+ if line=='parameters:':started=True;continue
151
+ if not started:
152
+ key,sep,value=line.partition(':')
153
+ if not sep or key in headers:raise ValueError('Malformed parameter-table header')
154
+ headers[key]=value.strip()
155
+ else:
156
+ columns=line.split('\t')
157
+ if len(columns)<4 or columns[0] or columns[1] not in SECTIONS:raise ValueError('Unsupported parameter-table row')
158
+ section,name,type_name=columns[1:4]
159
+ if not IDENTIFIER.fullmatch(name) or not type_name:raise ValueError('Invalid parameter identity')
160
+ pins.append(dict(name=name,type=type_name,section=section,direction=SECTIONS[section],native_attributes=columns[4:]))
161
+ name=headers.get('pouName','');kind=KINDS.get(headers.get('pouKind',''))
162
+ if not IDENTIFIER.fullmatch(name) or not kind or not started or not headers.get('parNum','').isdigit():raise ValueError('Unsupported parameter-table identity/count')
163
+ if len(pins)!=int(headers['parNum']) or len({p['name'].casefold() for p in pins})!=len(pins):raise ValueError('Parameter-table count/identity mismatch')
164
+ return dict(name=name,kind=kind,pins=pins,native_headers=headers)
165
+
166
+ def _registry(path):
167
+ rows=[]
168
+ for line in path.read_text(encoding='latin1').splitlines():
169
+ if not line.strip():continue
170
+ cols=line.split('\t');kind=KINDS.get(cols[0].upper())
171
+ if not kind or len(cols)<2 or not IDENTIFIER.fullmatch(cols[1]):raise ValueError('Unsupported library registry row')
172
+ alias=cols[5] if len(cols)>5 and cols[5] else cols[1]
173
+ if not IDENTIFIER.fullmatch(alias):raise ValueError('Unsupported library worksheet alias')
174
+ rows.append(dict(name=cols[1],kind=kind,alias=alias,hidden=any(c=='HIDDEN=1' for c in cols)))
175
+ if len({r['name'].casefold() for r in rows})!=len(rows):raise ValueError('Duplicate library registry name')
176
+ return rows
177
+
178
+ def _same_path(a,b):return str(Path(a).resolve()).casefold()==str(Path(b).resolve()).casefold()
179
+
180
+ def _user_library_declarations(root,pou):
181
+ if pou.declaration_stream_name() is not None:
182
+ return pou.declarations()
183
+ # User libraries can name their worksheets Variables/Code instead of
184
+ # <POU>V/<POU>. Bind the one declaration stream to its saved grid worksheet;
185
+ # do not infer directions from compiler heaps or unrelated streams.
186
+ container=pou.source();names=container.stream_names()
187
+ matches=[n for n in names if n.upper().endswith('.VB')]
188
+ if len(matches)!=1 or not matches[0][:-3]:
189
+ raise ValueError('User-library declaration worksheet absent or ambiguous')
190
+ declaration=matches[0];grid=declaration[:-3]+'.VGR'
191
+ grids=[n for n in names if n.casefold()==grid.casefold()]
192
+ if len(grids)!=1:raise ValueError('User-library declaration/grid worksheet pair absent or ambiguous')
193
+ document=parse_document(CompoundFile(Path(root)/'src.st1').read_stream('PROJECT.TRE').decode('latin1'))
194
+ expected=('POE/'+pou.name+'/'+grids[0]).casefold()
195
+ nodes=[n for n,_ in document.walk_with_ancestors()
196
+ if n.path.split('\t')[0].replace('\\','/').casefold()==expected]
197
+ if document.warnings or len(nodes)!=1 or document.lines[nodes[0].start_line].strip()!='8':
198
+ raise ValueError('User-library variable worksheet tree identity absent or ambiguous')
199
+ table=decode_declarations(container.read_stream(declaration),declaration)
200
+ count=read_grid_variable_count(container.read_stream(grids[0]))
201
+ if count is None:raise ValueError('User-library variable grid count unavailable')
202
+ table.warnings.extend(cross_check(table,count))
203
+ return table
204
+
205
+ def sources(root,native_libraries):
206
+ root=Path(root);project=Project(root)
207
+ native={r['name'].casefold():r for r in native_libraries}
208
+ if len(native)!=len(native_libraries):raise ValueError('Ambiguous native library inventory')
209
+ result=[dict(library='project',kind='PROJECT',root=root,registry=root/'LIST.POU',binding='active_project')]
210
+ lines=(root/'@LIBRARY.LST').read_text(encoding='latin1').splitlines()
211
+ if not lines or lines[0]!='Library List, V40':raise ValueError('Unsupported saved library registry')
212
+ used=set()
213
+ for line in lines[1:]:
214
+ if not line.strip():continue
215
+ fields=line.split(';')
216
+ if len(fields)!=4 or fields[0] not in ['FW','USER']:raise ValueError('Unsupported saved library reference')
217
+ kind,path,label,flags=fields;directory=Path(path)
218
+ library=directory.name if kind=='USER' else label
219
+ if not IDENTIFIER.fullmatch(library):raise ValueError('Unsupported library name')
220
+ row=native.get(library.casefold())
221
+ expected=Path(str(directory)+'.mwt') if kind=='USER' else directory/(label+'.fwl')
222
+ if row:
223
+ if not _same_path(row['full_name'],expected):raise ValueError('Saved/native library paths disagree: '+library)
224
+ used.add(library.casefold());binding='native_reference'
225
+ elif kind=='FW' and library.casefold() in ['iec','eclr']:
226
+ binding='saved_implicit_reference'
227
+ else:raise ValueError('Saved/native library inventories disagree: '+library)
228
+ registry=directory/'LIST.POU' if kind=='USER' else directory/(label+'.POU')
229
+ result.append(dict(library=library,kind=kind,root=directory,registry=registry,binding=binding,reference_file=expected,flags=flags))
230
+ if used!=set(native):raise ValueError('Native library absent from saved references')
231
+ return result
232
+
233
+ def inspect(root,native_libraries,name=None,library=None):
234
+ candidates=[];unavailable=[]
235
+ for source in sources(root,native_libraries):
236
+ if library and source['library'].casefold()!=library.casefold():continue
237
+ try:rows=_registry(source['registry'])
238
+ except (OSError,ValueError) as error:
239
+ unavailable.append(dict(library=source['library'],error=str(error)));continue
240
+ for row in rows:
241
+ if name and row['name'].casefold()!=name.casefold():continue
242
+ candidates.append({**row,'library':source['library'],'binding':source['binding'],'_source':source})
243
+ if not name:
244
+ return dict(blocks=[{k:v for k,v in row.items() if k!='_source'} for row in candidates],unavailable_libraries=unavailable,
245
+ evidence_kind='bound-installed-block-catalog',action_performed=False)
246
+ if len(candidates)!=1:raise ValueError('Block absent or ambiguous; specify its library. Candidates: '+str([(c['library'],c['name']) for c in candidates])+'; unavailable: '+str(unavailable))
247
+ row=candidates[0];source=row.pop('_source')
248
+ if source['kind']=='FW':
249
+ file=source['root']/'tmp.sto';container=CompoundFile(file)
250
+ matches=[n for n in container.stream_names() if n.casefold()==(row['alias']+'.PT').casefold()]
251
+ if len(matches)!=1:raise ValueError('Firmware parameter table absent or ambiguous')
252
+ data=container.read_stream(matches[0]);interface=parse_parameter_table(data)
253
+ if interface['name'].casefold()!=row['name'].casefold() or interface['kind']!=row['kind']:raise ValueError('Registry/parameter interface identity mismatch')
254
+ row.update(interface);row.update(source_stream=matches[0],source_sha256=hashlib.sha256(data).hexdigest(),origin='firmware_parameter_table')
255
+ else:
256
+ pou=Project(source['root']).pou(row['name'])
257
+ try:
258
+ table=_user_library_declarations(source['root'],pou) if source['kind']=='USER' else pou.declarations()
259
+ except CompressedStreamError:
260
+ if source['kind']!='USER':raise
261
+ row.update(_cached_library_interface(root,source['root'],pou))
262
+ file=Path(row['cache_file'])
263
+ row.update(source_sha256=hashlib.sha256(file.read_bytes()).hexdigest(),source_file=str(file),
264
+ reference_registry=str(source['registry']),registry_sha256=hashlib.sha256(source['registry'].read_bytes()).hexdigest(),
265
+ action_performed=False,note='Diagnostic cache declarations matched a saved compiler dependency. Freshness against the protected worksheet is unverified; do not use this interface for insertion. Native Build and complete source/library validation are still required.')
266
+ return row
267
+ if table.warnings:raise ValueError('Declaration interface parse warnings: '+str(table.warnings))
268
+ file=pou.source_path
269
+ row['pins']=[dict(name=v.name,type=v.type_name,section=v.section,direction=SECTIONS[v.section],initial_value=v.initial_value,description=v.description)
270
+ for v in table.variables if v.section in SECTIONS]
271
+ row.update(source_sha256=hashlib.sha256(file.read_bytes()).hexdigest(),source_stream=table.source_stream,origin='project_declarations' if source['kind']=='PROJECT' else 'user_library_declarations')
272
+ row.update(source_file=str(file),reference_registry=str(source['registry']),registry_sha256=hashlib.sha256(source['registry'].read_bytes()).hexdigest(),
273
+ evidence_kind='installed-declared-block-interface',action_performed=False,
274
+ note='Pin directions/types come from declarations or native parameter tables. Implicit references are identified separately. Verify the selected controller/firmware profile and Build/Make before machine use.')
275
+ return row